From 0ccd458585f43fbb324a02cfbe5c0d60b8386a36 Mon Sep 17 00:00:00 2001 From: "Marcel Mager (INST-IOT/ESB)" Date: Thu, 25 Aug 2016 09:35:18 +0200 Subject: [PATCH 01/28] Add support for multiple hashes in Issuer hash based authentication Signed-off-by: Marcel Mager (INST-IOT/ESB) --- ...bstractControllerAuthenticationFilter.java | 4 +- ...rPreAuthenticatedSecurityHeaderFilter.java | 27 ++++- ...okenSourceTrustAuthenticationProvider.java | 16 ++- .../security/PreAuthentificationFilter.java | 10 +- ...AuthenticatedSecurityHeaderFilterTest.java | 113 ++++++++++++++++++ ...ficateAuthenticationConfigurationItem.java | 8 +- 6 files changed, 160 insertions(+), 18 deletions(-) create mode 100644 hawkbit-security-integration/src/test/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilterTest.java diff --git a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/AbstractControllerAuthenticationFilter.java b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/AbstractControllerAuthenticationFilter.java index edf2ef034..c45cfb5e4 100644 --- a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/AbstractControllerAuthenticationFilter.java +++ b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/AbstractControllerAuthenticationFilter.java @@ -18,7 +18,7 @@ import org.slf4j.LoggerFactory; /** * An abstraction for all controller based security. Check if the tenant * configuration is enabled. - * + * * * */ @@ -50,7 +50,7 @@ public abstract class AbstractControllerAuthenticationFilter implements PreAuthe public abstract HeaderAuthentication getPreAuthenticatedPrincipal(TenantSecurityToken secruityToken); @Override - public abstract HeaderAuthentication getPreAuthenticatedCredentials(TenantSecurityToken secruityToken); + public abstract Object getPreAuthenticatedCredentials(TenantSecurityToken secruityToken); private final class SecurityConfigurationKeyTenantRunner implements TenantAware.TenantRunner { @Override diff --git a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilter.java b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilter.java index 6836b8a31..6fd13a1c6 100644 --- a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilter.java +++ b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilter.java @@ -8,6 +8,10 @@ */ package org.eclipse.hawkbit.security; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken; import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.tenancy.TenantAware; @@ -97,7 +101,7 @@ public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractCont } @Override - public HeaderAuthentication getPreAuthenticatedCredentials(final TenantSecurityToken secruityToken) { + public Object getPreAuthenticatedCredentials(final TenantSecurityToken secruityToken) { final String authorityNameConfigurationValue = tenantAware.runAsTenant(secruityToken.getTenant(), sslIssuerNameConfigTenantRunner); String controllerId = secruityToken.getControllerId(); @@ -108,7 +112,17 @@ public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractCont controllerId = secruityToken.getHeader(caCommonNameHeader); } - return new HeaderAuthentication(controllerId, authorityNameConfigurationValue); + String[] knownHashes = splitMultiHash(authorityNameConfigurationValue); + if (knownHashes.length > 1) { + Set multiHashes = new HashSet<>(); + final String cntlId = controllerId; + Arrays.asList(knownHashes) + .forEach(hashItem -> multiHashes.add(new HeaderAuthentication(cntlId, hashItem))); + return multiHashes; + + } else { + return new HeaderAuthentication(controllerId, authorityNameConfigurationValue); + } } /** @@ -117,12 +131,13 @@ public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractCont * It's ok if we find the the hash in any the trusted CA chain to accept * this request for this tenant. */ - private String getIssuerHashHeader(final TenantSecurityToken secruityToken, final String knownIssuerHash) { + private String getIssuerHashHeader(final TenantSecurityToken secruityToken, final String knownIssuerHashes) { // iterate over the headers until we get a null header. + String[] knownHashes = splitMultiHash(knownIssuerHashes); int iHeader = 1; String foundHash; while ((foundHash = secruityToken.getHeader(String.format(sslIssuerHashBasicHeader, iHeader))) != null) { - if (foundHash.equals(knownIssuerHash)) { + if (Arrays.asList(knownHashes).contains(foundHash)) { if (LOGGER.isTraceEnabled()) { LOGGER.trace("Found matching ssl issuer hash at position {}", iHeader); } @@ -148,4 +163,8 @@ public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractCont TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_AUTHORITY_NAME, String.class).getValue()); } } + + private static String[] splitMultiHash(String knownIssuerHashes) { + return knownIssuerHashes.split(";"); + } } diff --git a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthTokenSourceTrustAuthenticationProvider.java b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthTokenSourceTrustAuthenticationProvider.java index b4960737d..c653108a0 100644 --- a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthTokenSourceTrustAuthenticationProvider.java +++ b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthTokenSourceTrustAuthenticationProvider.java @@ -27,17 +27,17 @@ import org.springframework.security.web.authentication.preauth.PreAuthenticatedA * An spring authentication provider which supports authentication tokens of * type {@link PreAuthenticatedAuthenticationToken} created by the * {@link ControllerPreAuthenticatedSecurityHeaderFilter}. - * + * * Additionally to the authentication token providing the principal and the * credentials which must be match, this authentication provider can also check * the remote IP address of the request. - * + * * E.g. The request path is /controller/v1/{controllerId} then the controllerId * in the path is the principal. The credentials are the extracted information * from e.g. a certificate provided by an reverse proxy. Due this request is * only allowed from a specific source address this authentication manager can * also check the remote IP address of the request. - * + * * * */ @@ -58,7 +58,7 @@ public class PreAuthTokenSourceTrustAuthenticationProvider implements Authentica * Creates a new PreAuthTokenSourceTrustAuthenticationProvider with given * source IP addresses which are trusted and should be checked against the * request remote IP address. - * + * * @param authorizedSourceIps * a list of IP addresses. */ @@ -70,7 +70,7 @@ public class PreAuthTokenSourceTrustAuthenticationProvider implements Authentica * Creates a new PreAuthTokenSourceTrustAuthenticationProvider with given * source IP addresses which are trusted and should be checked against the * request remote IP address. - * + * * @param authorizedSourceIps * a list of IP addresses. */ @@ -104,6 +104,12 @@ public class PreAuthTokenSourceTrustAuthenticationProvider implements Authentica // proxy which extracted the CN from the certificate if (principal.equals(credentials)) { successAuthentication = checkSourceIPAddressIfNeccessary(tokenDetails); + + } else if (Collection.class.isAssignableFrom(credentials.getClass())) { + final Collection multiValueCredentials = (Collection) credentials; + if (multiValueCredentials.contains(principal)) { + successAuthentication = checkSourceIPAddressIfNeccessary(tokenDetails); + } } if (successAuthentication) { diff --git a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthentificationFilter.java b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthentificationFilter.java index 90f2acd50..f0801ff4d 100644 --- a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthentificationFilter.java +++ b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthentificationFilter.java @@ -22,7 +22,7 @@ public interface PreAuthentificationFilter { /** * Check if the filter is enabled. - * + * * @param secruityToken * the secruity info * @return is enabled diabled @@ -31,7 +31,7 @@ public interface PreAuthentificationFilter { /** * Extract the principal information from the current secruityToken. - * + * * @param secruityToken * the secruityToken * @return the extracted tenant and controller id @@ -40,17 +40,17 @@ public interface PreAuthentificationFilter { /** * Extract the principal credentials from the current secruityToken. - * + * * @param secruityToken * the secruityToken * @return the extracted tenant and controller id */ - HeaderAuthentication getPreAuthenticatedCredentials(TenantSecurityToken secruityToken); + Object getPreAuthenticatedCredentials(TenantSecurityToken secruityToken); /** * Allows to add additional authorities to the successful authenticated * token. - * + * * @return the authorities granted to the principal, or an empty collection * if the token has not been authenticated. Never null. * @see Authentication#getAuthorities() diff --git a/hawkbit-security-integration/src/test/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilterTest.java b/hawkbit-security-integration/src/test/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilterTest.java new file mode 100644 index 000000000..eb34d5253 --- /dev/null +++ b/hawkbit-security-integration/src/test/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilterTest.java @@ -0,0 +1,113 @@ +/** + * Copyright (c) 2015 Bosch Software Innovations 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 + */ +package org.eclipse.hawkbit.security; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.when; + +import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken; +import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken.FileResource; +import org.eclipse.hawkbit.repository.TenantConfigurationManagement; +import org.eclipse.hawkbit.repository.model.TenantConfigurationValue; +import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import ru.yandex.qatools.allure.annotations.Description; +import ru.yandex.qatools.allure.annotations.Features; +import ru.yandex.qatools.allure.annotations.Stories; + +@Features("Unit Tests - Security") +@Stories("Issuer hash based authentication") +@RunWith(MockitoJUnitRunner.class) +public class ControllerPreAuthenticatedSecurityHeaderFilterTest { + + private ControllerPreAuthenticatedSecurityHeaderFilter underTest; + + @Mock + private TenantConfigurationManagement tenantConfigurationManagementMock; + + @Mock + private TenantSecurityToken tenantSecurityTokenMock; + + private SecurityContextTenantAware tenantAware = new SecurityContextTenantAware(); + + private static final String CA_COMMON_NAME = "ca-cn"; + + private static final String X_SSL_ISSUER_HASH_1 = "X-Ssl-Issuer-Hash-1"; + + private static final String SINGLE_HASH = "hash1"; + + private static final String MULTI_HASH = "hash1;hash2;hash3"; + + private static final TenantConfigurationValue CONFIG_VALUE_SINGLE_HASH = TenantConfigurationValue + .builder().value(SINGLE_HASH).build(); + + private static final TenantConfigurationValue CONFIG_VALUE_MULTI_HASH = TenantConfigurationValue + .builder().value(MULTI_HASH).build(); + + @Before + public void before() { + underTest = new ControllerPreAuthenticatedSecurityHeaderFilter(CA_COMMON_NAME, "X-Ssl-Issuer-Hash-%d", + tenantConfigurationManagementMock, + tenantAware, new SystemSecurityContext(tenantAware)); + } + + @Test + @Description("Tests the filter for issuer hash based authentication with a single known hash") + public void testIssuerHashBasedAuthenticationWithSingleKnownHash() { + // prepare security token + final TenantSecurityToken securityToken = prepareSecurityToken(); + securityToken.getHeaders().put(X_SSL_ISSUER_HASH_1, SINGLE_HASH); + // use single known hash + when(tenantConfigurationManagementMock.getConfigurationValue( + eq(TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_AUTHORITY_NAME), eq(String.class))) + .thenReturn(CONFIG_VALUE_SINGLE_HASH); + assertNotNull(underTest.getPreAuthenticatedPrincipal(securityToken)); + } + + @Test + @Description("Tests the filter for issuer hash based authentication with multiple known hashes") + public void testIssuerHashBasedAuthenticationWithMultipleKnownHashes() { + // prepare security token + final TenantSecurityToken securityToken = prepareSecurityToken(); + securityToken.getHeaders().put(X_SSL_ISSUER_HASH_1, SINGLE_HASH); + // use multiple known hashes + when(tenantConfigurationManagementMock.getConfigurationValue( + eq(TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_AUTHORITY_NAME), eq(String.class))) + .thenReturn(CONFIG_VALUE_MULTI_HASH); + assertNotNull(underTest.getPreAuthenticatedPrincipal(securityToken)); + } + + @Test + @Description("Tests the filter for issuer hash based authentication with unknown hash") + public void testIssuerHashBasedAuthenticationWithUnknownHash() { + // prepare security token + final TenantSecurityToken securityToken = prepareSecurityToken(); + securityToken.getHeaders().put(X_SSL_ISSUER_HASH_1, "unknown"); + // use single known hash + when(tenantConfigurationManagementMock.getConfigurationValue( + eq(TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_AUTHORITY_NAME), eq(String.class))) + .thenReturn(CONFIG_VALUE_MULTI_HASH); + assertNull(underTest.getPreAuthenticatedPrincipal(securityToken)); + } + + private static TenantSecurityToken prepareSecurityToken() { + final TenantSecurityToken securityToken = new TenantSecurityToken("default", "1234", + FileResource.createFileResourceBySha1("12345")); + securityToken.getHeaders().put(CA_COMMON_NAME, "any"); + + return securityToken; + } + +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/authentication/CertificateAuthenticationConfigurationItem.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/authentication/CertificateAuthenticationConfigurationItem.java index c50b9f8fb..e041f5564 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/authentication/CertificateAuthenticationConfigurationItem.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/authentication/CertificateAuthenticationConfigurationItem.java @@ -63,13 +63,17 @@ public class CertificateAuthenticationConfigurationItem extends AbstractAuthenti final Label caRootAuthorityLabel = new LabelBuilder().name("SSL Issuer Hash:").buildLabel(); caRootAuthorityLabel.setDescription( "The SSL Issuer iRules.X509 hash, to validate against the controller request certifcate."); + caRootAuthorityLabel.setWidthUndefined(); - caRootAuthorityTextField = new TextFieldBuilder().immediate(true).maxLengthAllowed(128).buildTextComponent(); - caRootAuthorityTextField.setWidth("500px"); + caRootAuthorityTextField = new TextFieldBuilder().immediate(true).maxLengthAllowed(160).buildTextComponent(); + caRootAuthorityTextField.setWidth("100%"); caRootAuthorityTextField.addTextChangeListener(event -> caRootAuthorityChanged()); caRootAuthorityLayout.addComponent(caRootAuthorityLabel); + caRootAuthorityLayout.setExpandRatio(caRootAuthorityLabel, 0); caRootAuthorityLayout.addComponent(caRootAuthorityTextField); + caRootAuthorityLayout.setExpandRatio(caRootAuthorityTextField, 1); + caRootAuthorityLayout.setWidth("100%"); detailLayout.addComponent(caRootAuthorityLayout); From 5215580cd75557ad1cc95401ee7a4c992cdba3ae Mon Sep 17 00:00:00 2001 From: "Marcel Mager (INST-IOT/ESB)" Date: Thu, 15 Sep 2016 09:45:28 +0200 Subject: [PATCH 02/28] Fix code review findings: * adapt return type of #getPreAuthenticatedPrincipal to the #getPreAuthenticatedCredentials return type (both Object) * #splitMultiHash returns list instead of array Signed-off-by: Marcel Mager (INST-IOT/ESB) --- ...SourceTrustAuthenticationProviderTest.java | 32 +++++++++------- ...bstractControllerAuthenticationFilter.java | 2 +- ...rPreAuthenticatedSecurityHeaderFilter.java | 35 ++++++++---------- ...okenSourceTrustAuthenticationProvider.java | 14 ++++--- .../security/PreAuthentificationFilter.java | 2 +- ...AuthenticatedSecurityHeaderFilterTest.java | 37 ++++++++++++++++++- 6 files changed, 79 insertions(+), 43 deletions(-) diff --git a/hawkbit-http-security/src/test/java/org/eclipse/hawkbit/security/PreAuthTokenSourceTrustAuthenticationProviderTest.java b/hawkbit-http-security/src/test/java/org/eclipse/hawkbit/security/PreAuthTokenSourceTrustAuthenticationProviderTest.java index eb4cacf33..28e9708ac 100644 --- a/hawkbit-http-security/src/test/java/org/eclipse/hawkbit/security/PreAuthTokenSourceTrustAuthenticationProviderTest.java +++ b/hawkbit-http-security/src/test/java/org/eclipse/hawkbit/security/PreAuthTokenSourceTrustAuthenticationProviderTest.java @@ -21,6 +21,8 @@ import org.springframework.security.authentication.InsufficientAuthenticationExc import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; +import com.google.common.collect.Lists; + import ru.yandex.qatools.allure.annotations.Description; import ru.yandex.qatools.allure.annotations.Features; import ru.yandex.qatools.allure.annotations.Stories; @@ -45,8 +47,8 @@ public class PreAuthTokenSourceTrustAuthenticationProviderTest { public void principalAndCredentialsNotTheSameThrowsAuthenticationException() { final String principal = "controllerIdURL"; final String credentials = "controllerIdHeader"; - final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(principal, - credentials); + final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken( + principal, Lists.newArrayList(credentials)); token.setDetails(webAuthenticationDetailsMock); // test, should throw authentication exception @@ -64,11 +66,12 @@ public class PreAuthTokenSourceTrustAuthenticationProviderTest { public void principalAndCredentialsAreTheSameWithNoSourceIpCheckIsSuccessful() { final String principal = "controllerId"; final String credentials = "controllerId"; - final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(principal, - credentials); + final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken( + principal, Lists.newArrayList(credentials)); token.setDetails(webAuthenticationDetailsMock); - final Authentication authenticate = underTestWithoutSourceIpCheck.authenticate(token); + final Authentication authenticate = underTestWithoutSourceIpCheck + .authenticate(token); assertThat(authenticate.isAuthenticated()).isTrue(); } @@ -78,8 +81,8 @@ public class PreAuthTokenSourceTrustAuthenticationProviderTest { final String remoteAddress = "192.168.1.1"; final String principal = "controllerId"; final String credentials = "controllerId"; - final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(principal, - credentials); + final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken( + principal, Lists.newArrayList(credentials)); token.setDetails(webAuthenticationDetailsMock); when(webAuthenticationDetailsMock.getRemoteAddress()).thenReturn(remoteAddress); @@ -99,14 +102,15 @@ public class PreAuthTokenSourceTrustAuthenticationProviderTest { public void priniciapAndCredentialsAreTheSameAndSourceIpIsTrusted() { final String principal = "controllerId"; final String credentials = "controllerId"; - final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(principal, - credentials); + final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken( + principal, Lists.newArrayList(credentials)); token.setDetails(webAuthenticationDetailsMock); when(webAuthenticationDetailsMock.getRemoteAddress()).thenReturn(REQUEST_SOURCE_IP); // test, should throw authentication exception - final Authentication authenticate = underTestWithSourceIpCheck.authenticate(token); + final Authentication authenticate = underTestWithSourceIpCheck + .authenticate(token); assertThat(authenticate.isAuthenticated()).isTrue(); } @@ -116,8 +120,8 @@ public class PreAuthTokenSourceTrustAuthenticationProviderTest { "192.168.1.3" }; final String principal = "controllerId"; final String credentials = "controllerId"; - final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(principal, - credentials); + final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken( + principal, Lists.newArrayList(credentials)); token.setDetails(webAuthenticationDetailsMock); when(webAuthenticationDetailsMock.getRemoteAddress()).thenReturn(REQUEST_SOURCE_IP); @@ -135,8 +139,8 @@ public class PreAuthTokenSourceTrustAuthenticationProviderTest { final String[] trustedIPAddresses = new String[] { "192.168.1.1", "192.168.1.2", "192.168.1.3" }; final String principal = "controllerId"; final String credentials = "controllerId"; - final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(principal, - credentials); + final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken( + principal, Lists.newArrayList(credentials)); token.setDetails(webAuthenticationDetailsMock); when(webAuthenticationDetailsMock.getRemoteAddress()).thenReturn(REQUEST_SOURCE_IP); diff --git a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/AbstractControllerAuthenticationFilter.java b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/AbstractControllerAuthenticationFilter.java index c45cfb5e4..cf64e6c59 100644 --- a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/AbstractControllerAuthenticationFilter.java +++ b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/AbstractControllerAuthenticationFilter.java @@ -47,7 +47,7 @@ public abstract class AbstractControllerAuthenticationFilter implements PreAuthe } @Override - public abstract HeaderAuthentication getPreAuthenticatedPrincipal(TenantSecurityToken secruityToken); + public abstract Object getPreAuthenticatedPrincipal(TenantSecurityToken secruityToken); @Override public abstract Object getPreAuthenticatedCredentials(TenantSecurityToken secruityToken); diff --git a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilter.java b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilter.java index 6fd13a1c6..2f0ecaf6a 100644 --- a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilter.java +++ b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilter.java @@ -10,6 +10,7 @@ package org.eclipse.hawkbit.security; import java.util.Arrays; import java.util.HashSet; +import java.util.List; import java.util.Set; import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken; @@ -79,10 +80,9 @@ public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractCont } @Override - public HeaderAuthentication getPreAuthenticatedPrincipal(final TenantSecurityToken secruityToken) { + public Object getPreAuthenticatedPrincipal(final TenantSecurityToken secruityToken) { // retrieve the common name header and the authority name header from - // the http request and - // combine them together + // the http request and combine them together final String commonNameValue = secruityToken.getHeader(caCommonNameHeader); final String knownSslIssuerConfigurationValue = tenantAware.runAsTenant(secruityToken.getTenant(), sslIssuerNameConfigTenantRunner); @@ -106,23 +106,17 @@ public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractCont sslIssuerNameConfigTenantRunner); String controllerId = secruityToken.getControllerId(); // in case of legacy download artifact, the controller ID is not in the - // URL path, so then - // we just use the common name header + // URL path, so then we just use the common name header if (controllerId == null || "anonymous".equals(controllerId)) { controllerId = secruityToken.getHeader(caCommonNameHeader); } - String[] knownHashes = splitMultiHash(authorityNameConfigurationValue); - if (knownHashes.length > 1) { - Set multiHashes = new HashSet<>(); - final String cntlId = controllerId; - Arrays.asList(knownHashes) - .forEach(hashItem -> multiHashes.add(new HeaderAuthentication(cntlId, hashItem))); - return multiHashes; + List knownHashes = splitMultiHash(authorityNameConfigurationValue); - } else { - return new HeaderAuthentication(controllerId, authorityNameConfigurationValue); - } + Set multiHashes = new HashSet<>(); + final String cntlId = controllerId; + knownHashes.forEach(hashItem -> multiHashes.add(new HeaderAuthentication(cntlId, hashItem))); + return multiHashes; } /** @@ -132,12 +126,15 @@ public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractCont * this request for this tenant. */ private String getIssuerHashHeader(final TenantSecurityToken secruityToken, final String knownIssuerHashes) { + // there may be several knownIssuerHashes configured for the tenant + // separated by a semicolon + List knownHashes = splitMultiHash(knownIssuerHashes); + // iterate over the headers until we get a null header. - String[] knownHashes = splitMultiHash(knownIssuerHashes); int iHeader = 1; String foundHash; while ((foundHash = secruityToken.getHeader(String.format(sslIssuerHashBasicHeader, iHeader))) != null) { - if (Arrays.asList(knownHashes).contains(foundHash)) { + if (knownHashes.contains(foundHash)) { if (LOGGER.isTraceEnabled()) { LOGGER.trace("Found matching ssl issuer hash at position {}", iHeader); } @@ -164,7 +161,7 @@ public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractCont } } - private static String[] splitMultiHash(String knownIssuerHashes) { - return knownIssuerHashes.split(";"); + private static List splitMultiHash(String knownIssuerHashes) { + return Arrays.asList(knownIssuerHashes.split(";")); } } diff --git a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthTokenSourceTrustAuthenticationProvider.java b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthTokenSourceTrustAuthenticationProvider.java index c653108a0..7f69180be 100644 --- a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthTokenSourceTrustAuthenticationProvider.java +++ b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthTokenSourceTrustAuthenticationProvider.java @@ -97,14 +97,16 @@ public class PreAuthTokenSourceTrustAuthenticationProvider implements Authentica throw new BadCredentialsException("The provided principal and credentials are not match"); } - // check if principal equals credentials because we want to check if - // e.g. controllerId - // containing in the URL equals the controllerId in the special header - // set by the reverse - // proxy which extracted the CN from the certificate + // The credentials may either be of type HeaderAuthentication or of type + // Collection depending on the authentication mode + // in use (the latter is used in case of trusted reverse-proxy). + // It is checked whether principal equals credentials (respectively if + // credentials contains principal in case of collection) because we want + // to check if e.g. controllerId containing in the URL equals the + // controllerId in the special header set by the reverse-proxy which + // extracted the CN from the certificate. if (principal.equals(credentials)) { successAuthentication = checkSourceIPAddressIfNeccessary(tokenDetails); - } else if (Collection.class.isAssignableFrom(credentials.getClass())) { final Collection multiValueCredentials = (Collection) credentials; if (multiValueCredentials.contains(principal)) { diff --git a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthentificationFilter.java b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthentificationFilter.java index f0801ff4d..72f362fa0 100644 --- a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthentificationFilter.java +++ b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthentificationFilter.java @@ -36,7 +36,7 @@ public interface PreAuthentificationFilter { * the secruityToken * @return the extracted tenant and controller id */ - HeaderAuthentication getPreAuthenticatedPrincipal(TenantSecurityToken secruityToken); + Object getPreAuthenticatedPrincipal(TenantSecurityToken secruityToken); /** * Extract the principal credentials from the current secruityToken. diff --git a/hawkbit-security-integration/src/test/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilterTest.java b/hawkbit-security-integration/src/test/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilterTest.java index eb34d5253..dc99df7e7 100644 --- a/hawkbit-security-integration/src/test/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilterTest.java +++ b/hawkbit-security-integration/src/test/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilterTest.java @@ -12,6 +12,8 @@ import static org.junit.Assert.*; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.when; +import java.util.Collection; + import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken; import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken.FileResource; import org.eclipse.hawkbit.repository.TenantConfigurationManagement; @@ -102,10 +104,41 @@ public class ControllerPreAuthenticatedSecurityHeaderFilterTest { assertNull(underTest.getPreAuthenticatedPrincipal(securityToken)); } + @Test + @Description("Tests different values for issuer hash header and inspects the credentials") + public void useDifferentValuesForIssuerHashHeader() { + + // prepare security token + TenantSecurityToken securityToken = prepareSecurityToken(); + securityToken.getHeaders().put(X_SSL_ISSUER_HASH_1, "hash1"); + + when(tenantConfigurationManagementMock.getConfigurationValue( + eq(TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_AUTHORITY_NAME), eq(String.class))) + .thenReturn(CONFIG_VALUE_MULTI_HASH); + + HeaderAuthentication expected = new HeaderAuthentication("box1", "hash1"); + Collection credentials = (Collection) underTest + .getPreAuthenticatedCredentials(securityToken); + assertTrue(credentials.contains(expected)); + + Object principal = underTest.getPreAuthenticatedPrincipal(securityToken); + assertEquals(expected, principal); + + securityToken = prepareSecurityToken(); + securityToken.getHeaders().put(X_SSL_ISSUER_HASH_1, "hash2"); + expected = new HeaderAuthentication("box1", "hash2"); + credentials = (Collection) underTest.getPreAuthenticatedCredentials(securityToken); + assertTrue(credentials.contains(expected)); + + principal = underTest.getPreAuthenticatedPrincipal(securityToken); + assertEquals(expected, principal); + + } + private static TenantSecurityToken prepareSecurityToken() { - final TenantSecurityToken securityToken = new TenantSecurityToken("default", "1234", + final TenantSecurityToken securityToken = new TenantSecurityToken("DEFAULT", "box1", FileResource.createFileResourceBySha1("12345")); - securityToken.getHeaders().put(CA_COMMON_NAME, "any"); + securityToken.getHeaders().put(CA_COMMON_NAME, "box1"); return securityToken; } From a0c5915ce651603d4614a3e55c9aa3aeec2eeec7 Mon Sep 17 00:00:00 2001 From: "Marcel Mager (INST-IOT/ESB)" Date: Wed, 21 Sep 2016 16:40:32 +0200 Subject: [PATCH 03/28] Reduce cyclomatic complexity by extracting method Signed-off-by: Marcel Mager (INST-IOT/ESB) --- ...okenSourceTrustAuthenticationProvider.java | 48 ++++++++++++------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthTokenSourceTrustAuthenticationProvider.java b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthTokenSourceTrustAuthenticationProvider.java index 7f69180be..78cfdfd47 100644 --- a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthTokenSourceTrustAuthenticationProvider.java +++ b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthTokenSourceTrustAuthenticationProvider.java @@ -87,7 +87,6 @@ public class PreAuthTokenSourceTrustAuthenticationProvider implements Authentica return null; } - boolean successAuthentication = false; final PreAuthenticatedAuthenticationToken token = (PreAuthenticatedAuthenticationToken) authentication; final Object credentials = token.getCredentials(); final Object principal = token.getPrincipal(); @@ -97,22 +96,7 @@ public class PreAuthTokenSourceTrustAuthenticationProvider implements Authentica throw new BadCredentialsException("The provided principal and credentials are not match"); } - // The credentials may either be of type HeaderAuthentication or of type - // Collection depending on the authentication mode - // in use (the latter is used in case of trusted reverse-proxy). - // It is checked whether principal equals credentials (respectively if - // credentials contains principal in case of collection) because we want - // to check if e.g. controllerId containing in the URL equals the - // controllerId in the special header set by the reverse-proxy which - // extracted the CN from the certificate. - if (principal.equals(credentials)) { - successAuthentication = checkSourceIPAddressIfNeccessary(tokenDetails); - } else if (Collection.class.isAssignableFrom(credentials.getClass())) { - final Collection multiValueCredentials = (Collection) credentials; - if (multiValueCredentials.contains(principal)) { - successAuthentication = checkSourceIPAddressIfNeccessary(tokenDetails); - } - } + boolean successAuthentication = calculateAuthenticationSuccess(principal, credentials, tokenDetails); if (successAuthentication) { final Collection controllerAuthorities = new ArrayList<>(); @@ -126,6 +110,36 @@ public class PreAuthTokenSourceTrustAuthenticationProvider implements Authentica throw new BadCredentialsException("The provided principal and credentials are not match"); } + + /** + * + * The credentials may either be of type HeaderAuthentication or of type + * Collection depending on the authentication mode in + * use (the latter is used in case of trusted reverse-proxy). It is checked + * whether principal equals credentials (respectively if credentials + * contains principal in case of collection) because we want to check if + * e.g. controllerId containing in the URL equals the controllerId in the + * special header set by the reverse-proxy which extracted the CN from the + * certificate. + * + * @param principal + * @param credentials + * @param tokenDetails + * @return + */ + private boolean calculateAuthenticationSuccess(Object principal, Object credentials, Object tokenDetails) { + boolean successAuthentication = false; + if (principal.equals(credentials)) { + successAuthentication = checkSourceIPAddressIfNeccessary(tokenDetails); + } else if (Collection.class.isAssignableFrom(credentials.getClass())) { + final Collection multiValueCredentials = (Collection) credentials; + if (multiValueCredentials.contains(principal)) { + successAuthentication = checkSourceIPAddressIfNeccessary(tokenDetails); + } + } + + return successAuthentication; + } private boolean checkSourceIPAddressIfNeccessary(final Object tokenDetails) { boolean success = authorizedSourceIps == null; From 54c4c8c481fabc2c3a27461fcad2a8609906f92a Mon Sep 17 00:00:00 2001 From: "Marcel Mager (INST-IOT/ESB)" Date: Thu, 22 Sep 2016 16:02:27 +0200 Subject: [PATCH 04/28] Review fixes of code quality. Signed-off-by: Marcel Mager (INST-IOT/ESB) --- ...rPreAuthenticatedSecurityHeaderFilter.java | 4 +- ...okenSourceTrustAuthenticationProvider.java | 41 ++++++++++--------- ...AuthenticatedSecurityHeaderFilterTest.java | 16 +++++--- 3 files changed, 34 insertions(+), 27 deletions(-) diff --git a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilter.java b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilter.java index 2f0ecaf6a..c3d6bd3bd 100644 --- a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilter.java +++ b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilter.java @@ -20,6 +20,8 @@ import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.collect.Sets; + /** * An pre-authenticated processing filter which extracts the principal from a * request URI and the credential from a request header in a the @@ -113,7 +115,7 @@ public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractCont List knownHashes = splitMultiHash(authorityNameConfigurationValue); - Set multiHashes = new HashSet<>(); + Set multiHashes = Sets.newHashSetWithExpectedSize(knownHashes.size()); final String cntlId = controllerId; knownHashes.forEach(hashItem -> multiHashes.add(new HeaderAuthentication(cntlId, hashItem))); return multiHashes; diff --git a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthTokenSourceTrustAuthenticationProvider.java b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthTokenSourceTrustAuthenticationProvider.java index 78cfdfd47..4abf0deb0 100644 --- a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthTokenSourceTrustAuthenticationProvider.java +++ b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthTokenSourceTrustAuthenticationProvider.java @@ -110,34 +110,35 @@ public class PreAuthTokenSourceTrustAuthenticationProvider implements Authentica throw new BadCredentialsException("The provided principal and credentials are not match"); } - + /** - * - * The credentials may either be of type HeaderAuthentication or of type - * Collection depending on the authentication mode in - * use (the latter is used in case of trusted reverse-proxy). It is checked - * whether principal equals credentials (respectively if credentials - * contains principal in case of collection) because we want to check if - * e.g. controllerId containing in the URL equals the controllerId in the - * special header set by the reverse-proxy which extracted the CN from the - * certificate. - * - * @param principal - * @param credentials - * @param tokenDetails - * @return - */ + * + * The credentials may either be of type HeaderAuthentication or of type + * Collection depending on the authentication mode in + * use (the latter is used in case of trusted reverse-proxy). It is checked + * whether principal equals credentials (respectively if credentials + * contains principal in case of collection) because we want to check if + * e.g. controllerId containing in the URL equals the controllerId in the + * special header set by the reverse-proxy which extracted the CN from the + * certificate. + * + * @param principal + * @param credentials + * @param tokenDetails + * @return true if authentication succeeded, otherwise + * false + */ private boolean calculateAuthenticationSuccess(Object principal, Object credentials, Object tokenDetails) { boolean successAuthentication = false; - if (principal.equals(credentials)) { - successAuthentication = checkSourceIPAddressIfNeccessary(tokenDetails); - } else if (Collection.class.isAssignableFrom(credentials.getClass())) { + if (Collection.class.isAssignableFrom(credentials.getClass())) { final Collection multiValueCredentials = (Collection) credentials; if (multiValueCredentials.contains(principal)) { successAuthentication = checkSourceIPAddressIfNeccessary(tokenDetails); } + } else if (principal.equals(credentials)) { + successAuthentication = checkSourceIPAddressIfNeccessary(tokenDetails); } - + return successAuthentication; } diff --git a/hawkbit-security-integration/src/test/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilterTest.java b/hawkbit-security-integration/src/test/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilterTest.java index dc99df7e7..59605b76d 100644 --- a/hawkbit-security-integration/src/test/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilterTest.java +++ b/hawkbit-security-integration/src/test/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilterTest.java @@ -8,7 +8,10 @@ */ package org.eclipse.hawkbit.security; -import static org.junit.Assert.*; + +//import static org.junit.Assert.*; +import static org.fest.assertions.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.when; @@ -75,7 +78,7 @@ public class ControllerPreAuthenticatedSecurityHeaderFilterTest { when(tenantConfigurationManagementMock.getConfigurationValue( eq(TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_AUTHORITY_NAME), eq(String.class))) .thenReturn(CONFIG_VALUE_SINGLE_HASH); - assertNotNull(underTest.getPreAuthenticatedPrincipal(securityToken)); + assertThat(underTest.getPreAuthenticatedPrincipal(securityToken)).isNotNull(); } @Test @@ -88,7 +91,7 @@ public class ControllerPreAuthenticatedSecurityHeaderFilterTest { when(tenantConfigurationManagementMock.getConfigurationValue( eq(TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_AUTHORITY_NAME), eq(String.class))) .thenReturn(CONFIG_VALUE_MULTI_HASH); - assertNotNull(underTest.getPreAuthenticatedPrincipal(securityToken)); + assertThat(underTest.getPreAuthenticatedPrincipal(securityToken)).isNotNull(); } @Test @@ -101,7 +104,8 @@ public class ControllerPreAuthenticatedSecurityHeaderFilterTest { when(tenantConfigurationManagementMock.getConfigurationValue( eq(TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_AUTHORITY_NAME), eq(String.class))) .thenReturn(CONFIG_VALUE_MULTI_HASH); - assertNull(underTest.getPreAuthenticatedPrincipal(securityToken)); + assertThat(underTest.getPreAuthenticatedPrincipal(securityToken)).isNull(); + ; } @Test @@ -119,7 +123,7 @@ public class ControllerPreAuthenticatedSecurityHeaderFilterTest { HeaderAuthentication expected = new HeaderAuthentication("box1", "hash1"); Collection credentials = (Collection) underTest .getPreAuthenticatedCredentials(securityToken); - assertTrue(credentials.contains(expected)); + assertThat(credentials.contains(expected)).isTrue(); Object principal = underTest.getPreAuthenticatedPrincipal(securityToken); assertEquals(expected, principal); @@ -128,7 +132,7 @@ public class ControllerPreAuthenticatedSecurityHeaderFilterTest { securityToken.getHeaders().put(X_SSL_ISSUER_HASH_1, "hash2"); expected = new HeaderAuthentication("box1", "hash2"); credentials = (Collection) underTest.getPreAuthenticatedCredentials(securityToken); - assertTrue(credentials.contains(expected)); + assertThat(credentials.contains(expected)).isTrue(); principal = underTest.getPreAuthenticatedPrincipal(securityToken); assertEquals(expected, principal); From 7176f93ca4bf25089a0a0c3a2a51b7b0be99ee11 Mon Sep 17 00:00:00 2001 From: "Marcel Mager (INST-IOT/ESB)" Date: Fri, 23 Sep 2016 10:11:36 +0200 Subject: [PATCH 05/28] Fix code smells. Signed-off-by: Marcel Mager (INST-IOT/ESB) --- .../ControllerPreAuthenticatedSecurityHeaderFilter.java | 1 - .../PreAuthTokenSourceTrustAuthenticationProvider.java | 6 +++--- .../ControllerPreAuthenticatedSecurityHeaderFilterTest.java | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilter.java b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilter.java index c3d6bd3bd..6965afe3f 100644 --- a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilter.java +++ b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilter.java @@ -9,7 +9,6 @@ package org.eclipse.hawkbit.security; import java.util.Arrays; -import java.util.HashSet; import java.util.List; import java.util.Set; diff --git a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthTokenSourceTrustAuthenticationProvider.java b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthTokenSourceTrustAuthenticationProvider.java index 4abf0deb0..0809f6691 100644 --- a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthTokenSourceTrustAuthenticationProvider.java +++ b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthTokenSourceTrustAuthenticationProvider.java @@ -111,7 +111,7 @@ public class PreAuthTokenSourceTrustAuthenticationProvider implements Authentica throw new BadCredentialsException("The provided principal and credentials are not match"); } - /** + /** * * The credentials may either be of type HeaderAuthentication or of type * Collection depending on the authentication mode in @@ -129,7 +129,7 @@ public class PreAuthTokenSourceTrustAuthenticationProvider implements Authentica * false */ private boolean calculateAuthenticationSuccess(Object principal, Object credentials, Object tokenDetails) { - boolean successAuthentication = false; + boolean successAuthentication = false; if (Collection.class.isAssignableFrom(credentials.getClass())) { final Collection multiValueCredentials = (Collection) credentials; if (multiValueCredentials.contains(principal)) { @@ -139,7 +139,7 @@ public class PreAuthTokenSourceTrustAuthenticationProvider implements Authentica successAuthentication = checkSourceIPAddressIfNeccessary(tokenDetails); } - return successAuthentication; + return successAuthentication; } private boolean checkSourceIPAddressIfNeccessary(final Object tokenDetails) { diff --git a/hawkbit-security-integration/src/test/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilterTest.java b/hawkbit-security-integration/src/test/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilterTest.java index 59605b76d..1275e44b0 100644 --- a/hawkbit-security-integration/src/test/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilterTest.java +++ b/hawkbit-security-integration/src/test/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilterTest.java @@ -126,7 +126,7 @@ public class ControllerPreAuthenticatedSecurityHeaderFilterTest { assertThat(credentials.contains(expected)).isTrue(); Object principal = underTest.getPreAuthenticatedPrincipal(securityToken); - assertEquals(expected, principal); + assertEquals("hash1 expected in principal!", expected, principal); securityToken = prepareSecurityToken(); securityToken.getHeaders().put(X_SSL_ISSUER_HASH_1, "hash2"); @@ -135,7 +135,7 @@ public class ControllerPreAuthenticatedSecurityHeaderFilterTest { assertThat(credentials.contains(expected)).isTrue(); principal = underTest.getPreAuthenticatedPrincipal(securityToken); - assertEquals(expected, principal); + assertEquals("hash2 expected in principal!", expected, principal); } From 03e2ee81b86487fa79bac937dabbefe8ca3f7722 Mon Sep 17 00:00:00 2001 From: Dominik Herbst Date: Thu, 1 Sep 2016 10:19:55 +0200 Subject: [PATCH 06/28] Added auto assign distribution set to target filter query feature Signed-off-by: Dominik Herbst --- .gitignore | 4 + .../repository/TargetFilterQueryFields.java | 70 +++ .../targetfilter/MgmtTargetFilterQuery.java | 89 +++ .../MgmtTargetFilterQueryRequestBody.java | 50 ++ .../rest/api/MgmtDistributionSetRestApi.java | 32 ++ .../mgmt/rest/api/MgmtRestConstants.java | 5 + .../api/MgmtTargetFilterQueryRestApi.java | 156 ++++++ .../resource/MgmtDistributionSetResource.java | 37 ++ .../resource/MgmtTargetFilterQueryMapper.java | 95 ++++ .../MgmtTargetFilterQueryResource.java | 180 +++++++ .../MgmtDistributionSetResourceTest.java | 24 +- .../MgmtTargetFilterQueryResourceTest.java | 362 +++++++++++++ .../repository/AutoAssignProperties.java | 50 ++ .../repository/DeploymentManagement.java | 28 +- .../hawkbit/repository/EntityFactory.java | 24 + .../TargetFilterQueryManagement.java | 65 ++- .../hawkbit/repository/TargetManagement.java | 29 + .../repository/model/DistributionSet.java | 5 + .../repository/model/TargetFilterQuery.java | 12 + .../jpa/JpaDeploymentManagement.java | 35 +- .../jpa/JpaDistributionSetManagement.java | 7 +- .../repository/jpa/JpaEntityFactory.java | 10 + .../repository/jpa/JpaSystemManagement.java | 4 + .../jpa/JpaTargetFilterQueryManagement.java | 46 +- .../repository/jpa/JpaTargetManagement.java | 26 + .../jpa/TargetFilterQueryRepository.java | 13 + .../jpa/autoassign/AutoAssignChecker.java | 168 ++++++ .../jpa/autoassign/AutoAssignScheduler.java | 77 +++ .../jpa/model/JpaDistributionSet.java | 9 + .../jpa/model/JpaTargetFilterQuery.java | 36 ++ .../TargetFilterQuerySpecification.java | 25 + .../specifications/TargetSpecifications.java | 21 + .../H2/V1_8_0__auto_assign_ds_filter__H2.sql | 8 + .../V1_8_0__auto_assign_ds_filter__MYSQL.sql | 8 + .../jpa/TargetFilterQueryManagenmentTest.java | 168 +++++- .../jpa/TargetManagementSearchTest.java | 30 +- .../jpa/autoassign/AutoAssignCheckerTest.java | 157 ++++++ .../hawkbit/ui/common/CommonDialogWindow.java | 4 +- .../TargetFilterQueryDetailsTable.java | 107 ++++ .../ui/components/ProxyDistribution.java | 30 ++ .../ui/components/ProxyTargetFilter.java | 8 + .../dstable/DistributionSetDetails.java | 14 + .../dstable/ManageDistBeanQuery.java | 25 +- .../DistributionSetSelectTable.java | 182 +++++++ .../DistributionSetSelectWindow.java | 307 +++++++++++ .../TargetFilterBeanQuery.java | 11 +- .../filtermanagement/TargetFilterTable.java | 57 +- .../hawkbit/ui/utils/SPUIDefinitions.java | 5 + .../ui/utils/SPUILabelDefinitions.java | 4 + .../ui/utils/UIComponentIdProvider.java | 20 + .../hawkbit/customstyles/table-common.scss | 1 + .../src/main/resources/messages.properties | 91 ++-- .../src/main/resources/messages_de.properties | 505 ------------------ .../src/main/resources/messages_en.properties | 501 ----------------- 54 files changed, 2922 insertions(+), 1115 deletions(-) create mode 100644 hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/TargetFilterQueryFields.java create mode 100644 hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/targetfilter/MgmtTargetFilterQuery.java create mode 100644 hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/targetfilter/MgmtTargetFilterQueryRequestBody.java create mode 100644 hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtTargetFilterQueryRestApi.java create mode 100644 hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryMapper.java create mode 100644 hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryResource.java create mode 100644 hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryResourceTest.java create mode 100644 hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/AutoAssignProperties.java create mode 100644 hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignChecker.java create mode 100644 hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignScheduler.java create mode 100644 hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/H2/V1_8_0__auto_assign_ds_filter__H2.sql create mode 100644 hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/MYSQL/V1_8_0__auto_assign_ds_filter__MYSQL.sql create mode 100644 hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignCheckerTest.java create mode 100644 hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/detailslayout/TargetFilterQueryDetailsTable.java create mode 100644 hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/DistributionSetSelectTable.java create mode 100644 hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/DistributionSetSelectWindow.java delete mode 100644 hawkbit-ui/src/main/resources/messages_de.properties delete mode 100644 hawkbit-ui/src/main/resources/messages_en.properties diff --git a/.gitignore b/.gitignore index e49219859..9ccbafa9d 100644 --- a/.gitignore +++ b/.gitignore @@ -38,6 +38,10 @@ local.properties .settings/ .loadpath +# IDEA +*.iml +.idea + # External tool builders .externalToolBuilders/ diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/TargetFilterQueryFields.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/TargetFilterQueryFields.java new file mode 100644 index 000000000..1cee4c369 --- /dev/null +++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/TargetFilterQueryFields.java @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2015 Bosch Software Innovations 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 + */ +package org.eclipse.hawkbit.repository; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * Describing the fields of the Target model which can be used in the REST API + * e.g. for sorting etc. + * + */ +public enum TargetFilterQueryFields implements FieldNameProvider { + + /** + * The id field. + */ + ID("id"), + + /** + * The name field. + */ + NAME("name"), + + /** + * distribution set which is set as auto assign distribution set + */ + AUTOASSIGNDISTRIBUTIONSET("autoAssignDistributionSet", "name", "version"); + + + private final String fieldName; + private List subEntityAttributes; + private boolean mapField; + + TargetFilterQueryFields(final String fieldName) { + this(fieldName, false, Collections.emptyList()); + } + + TargetFilterQueryFields(final String fieldName, final String... subEntityAttribues) { + this(fieldName, false, Arrays.asList(subEntityAttribues)); + } + + TargetFilterQueryFields(final String fieldName, final boolean mapField, final List subEntityAttribues) { + this.fieldName = fieldName; + this.mapField = mapField; + this.subEntityAttributes = subEntityAttribues; + } + + @Override + public List getSubEntityAttributes() { + return subEntityAttributes; + } + + @Override + public boolean isMap() { + return mapField; + } + + @Override + public String getFieldName() { + return fieldName; + } +} diff --git a/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/targetfilter/MgmtTargetFilterQuery.java b/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/targetfilter/MgmtTargetFilterQuery.java new file mode 100644 index 000000000..c5378a195 --- /dev/null +++ b/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/targetfilter/MgmtTargetFilterQuery.java @@ -0,0 +1,89 @@ +/** + * Copyright (c) 2011-2015 Bosch Software Innovations GmbH, Germany. All rights reserved. + */ +package org.eclipse.hawkbit.mgmt.json.model.targetfilter; + +import org.eclipse.hawkbit.mgmt.json.model.MgmtBaseEntity; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * A json annotated rest model for Target to RESTful API representation. + * + */ +@JsonInclude(Include.ALWAYS) +@JsonIgnoreProperties(ignoreUnknown = true) +public class MgmtTargetFilterQuery extends MgmtBaseEntity { + + @JsonProperty(value = "id", required = true) + private Long filterId; + + @JsonProperty + private String name; + + @JsonProperty + private String query; + + @JsonProperty + private Long autoAssignDistributionSet; + + /** + * @return the filterId + */ + public Long getFilterId() { + return filterId; + } + + /** + * @param filterId + * the filterId to set + */ + public void setFilterId(final Long filterId) { + this.filterId = filterId; + } + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name + * the name to set + */ + public void setName(final String name) { + this.name = name; + } + + /** + * @return the query + */ + public String getQuery() { + return query; + } + + /** + * @param query + * the query to set + */ + @JsonIgnore + public void setQuery(final String query) { + this.query = query; + } + + public Long getAutoAssignDistributionSet() { + return autoAssignDistributionSet; + } + + @JsonIgnore + public void setAutoAssignDistributionSet(final Long autoAssignDistributionSet) { + this.autoAssignDistributionSet = autoAssignDistributionSet; + } + +} diff --git a/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/targetfilter/MgmtTargetFilterQueryRequestBody.java b/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/targetfilter/MgmtTargetFilterQueryRequestBody.java new file mode 100644 index 000000000..a87c6fd2f --- /dev/null +++ b/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/targetfilter/MgmtTargetFilterQueryRequestBody.java @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2011-2015 Bosch Software Innovations GmbH, Germany. All rights reserved. + */ +package org.eclipse.hawkbit.mgmt.json.model.targetfilter; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Request body for target PUT/POST commands. + * + */ +public class MgmtTargetFilterQueryRequestBody { + @JsonProperty(required = true) + private String name; + + @JsonProperty(required = true) + private String query; + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name + * the name to set + */ + public MgmtTargetFilterQueryRequestBody setName(final String name) { + this.name = name; + return this; + } + + /** + * @return the filter query + */ + public String getQuery() { + return query; + } + + /** + * @param query + * the filter query + */ + public void setQuery(String query) { + this.query = query; + } + +} diff --git a/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtDistributionSetRestApi.java b/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtDistributionSetRestApi.java index d3439adc6..13eff9831 100644 --- a/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtDistributionSetRestApi.java +++ b/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtDistributionSetRestApi.java @@ -20,6 +20,7 @@ import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtTargetAssignmentR import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.MgmtSoftwareModule; import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.MgmtSoftwareModuleAssigment; import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTarget; +import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQuery; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PathVariable; @@ -179,6 +180,37 @@ public interface MgmtDistributionSetRestApi { @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) final String sortParam, @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) final String rsqlParam); + /** + * Handles the GET request to retrieve target filter queries that have the + * given distribution set as auto assign DS. + * + * @param distributionSetId + * the ID of the distribution set to retrieve the assigned + * targets + * @param pagingOffsetParam + * the offset of list of targets for pagination, might not be + * present in the rest request then default value will be applied + * @param pagingLimitParam + * the limit of the paged request, might not be present in the + * rest request then default value will be applied + * @param sortParam + * the sorting parameter in the request URL, syntax + * {@code field:direction, field:direction} + * @param rsqlParam + * the search name parameter in the request URL, syntax + * {@code q=myFilter} + * @return status OK if get request is successful with the paged list of + * targets + */ + @RequestMapping(method = RequestMethod.GET, value = "/{distributionSetId}/autoAssignTargetFilters", produces = { + MediaType.APPLICATION_JSON_VALUE, "application/hal+json" }) + ResponseEntity> getAutoAssignTargetFilterQueries( + @PathVariable("distributionSetId") final Long distributionSetId, + @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) final int pagingOffsetParam, + @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) final int pagingLimitParam, + @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) final String sortParam, + @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) final String rsqlParam); + /** * Handles the POST request of assigning multiple targets to a single * distribution set. diff --git a/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtRestConstants.java b/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtRestConstants.java index e8c25403f..a46fbf79a 100644 --- a/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtRestConstants.java +++ b/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtRestConstants.java @@ -104,6 +104,11 @@ public final class MgmtRestConstants { public static final String DISTRIBUTIONSET_TAG_V1_REQUEST_MAPPING = BASE_V1_REQUEST_MAPPING + "/distributionsettags"; + /** + * The target URL mapping rest resource. + */ + public static final String TARGET_FILTER_V1_REQUEST_MAPPING = BASE_V1_REQUEST_MAPPING + "/targetfilters"; + /** * The tag URL mapping rest resource. */ diff --git a/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtTargetFilterQueryRestApi.java b/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtTargetFilterQueryRestApi.java new file mode 100644 index 000000000..7b7d51353 --- /dev/null +++ b/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtTargetFilterQueryRestApi.java @@ -0,0 +1,156 @@ +/** + * Copyright (c) 2015 Bosch Software Innovations 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 + */ +package org.eclipse.hawkbit.mgmt.rest.api; + +import org.eclipse.hawkbit.mgmt.json.model.MgmtId; +import org.eclipse.hawkbit.mgmt.json.model.PagedList; +import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSet; +import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQuery; +import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQueryRequestBody; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; + +/** + * Api for handling target operations. + */ +@RequestMapping(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING) +public interface MgmtTargetFilterQueryRestApi { + + /** + * Handles the GET request of retrieving a single target filter. + * + * @param filterId + * the ID of the target filter to retrieve + * @return a single target with status OK. + */ + + @RequestMapping(method = RequestMethod.GET, value = "/{filterId}", produces = { "application/hal+json", + MediaType.APPLICATION_JSON_VALUE }) + ResponseEntity getFilter(@PathVariable("filterId") final Long filterId); + + /** + * Handles the GET request of retrieving all filters. + * + * @param pagingOffsetParam + * the offset of list of targets for pagination, might not be + * present in the rest request then default value will be applied + * @param pagingLimitParam + * the limit of the paged request, might not be present in the + * rest request then default value will be applied + * @param sortParam + * the sorting parameter in the request URL, syntax + * {@code field:direction, field:direction} + * @param rsqlParam + * the search parameter in the request URL, syntax + * {@code q=name==abc} + * @return a list of all targets for a defined or default page request with + * status OK. The response is always paged. In any failure the + * JsonResponseExceptionHandler is handling the response. + */ + + @RequestMapping(method = RequestMethod.GET, produces = { "application/hal+json", MediaType.APPLICATION_JSON_VALUE }) + ResponseEntity> getFilters( + @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) final int pagingOffsetParam, + @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) final int pagingLimitParam, + @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) final String sortParam, + @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) final String rsqlParam); + + /** + * Handles the POST request of creating new target filters. The request body + * must always be a list of target filters. + * + * @param filter + * the filters to be created. + * @return In case all filters were successfully created the ResponseEntity + * with status code 201 with a list of successfully created entities + * is returned. In any failure the JsonResponseExceptionHandler is + * handling the response. + */ + @RequestMapping(method = RequestMethod.POST, consumes = { "application/hal+json", + MediaType.APPLICATION_JSON_VALUE }, produces = { "application/hal+json", MediaType.APPLICATION_JSON_VALUE }) + ResponseEntity createFilter(@RequestBody final MgmtTargetFilterQueryRequestBody filter); + + /** + * Handles the PUT request of updating a target filter. The ID is within the + * URL path of the request. A given ID in the request body is ignored. It's + * not possible to set fields to {@code null} values. + * + * @param filterId + * the path parameter which contains the ID of the target filter + * @param targetFilterRest + * the request body which contains the fields which should be + * updated, fields which are not given are ignored for the + * update. + * @return the updated target filter response which contains all fields + * including fields which have not been updated + */ + @RequestMapping(method = RequestMethod.PUT, value = "/{filterId}", consumes = { "application/hal+json", + MediaType.APPLICATION_JSON_VALUE }, produces = { "application/hal+json", MediaType.APPLICATION_JSON_VALUE }) + ResponseEntity updateFilter(@PathVariable("filterId") final Long filterId, + @RequestBody final MgmtTargetFilterQueryRequestBody targetFilterRest); + + /** + * Handles the DELETE request of deleting a target filter. + * + * @param filterId + * the ID of the target filter to be deleted + * @return If the given controllerId could exists and could be deleted Http + * OK. In any failure the JsonResponseExceptionHandler is handling + * the response. + */ + @RequestMapping(method = RequestMethod.DELETE, value = "/{filterId}", produces = { "application/hal+json", + MediaType.APPLICATION_JSON_VALUE }) + ResponseEntity deleteFilter(@PathVariable("filterId") final Long filterId); + + /** + * Handles the GET request of retrieving the distribution set for auto + * assignment of an specific target filter. + * + * @param filterId + * the ID of the target to retrieve the assigned distribution + * @return the assigned distribution set with status OK, if none is assigned + * than {@code null} content (e.g. "{}") + */ + @RequestMapping(method = RequestMethod.GET, value = "/{filterId}/autoAssignDS", produces = { "application/hal+json", + MediaType.APPLICATION_JSON_VALUE }) + ResponseEntity getAssignedDistributionSet(@PathVariable("filterId") final Long filterId); + + /** + * Handles the POST request for changing distribution set for auto + * assignment of a target filter. + * + * @param filterId + * of the target to change + * @param dsId + * of the Id of the auto assign distribution set + * @return http status + */ + @RequestMapping(method = RequestMethod.POST, value = "/{filterId}/autoAssignDS", consumes = { + "application/hal+json", + MediaType.APPLICATION_JSON_VALUE }, produces = { "application/hal+json", MediaType.APPLICATION_JSON_VALUE }) + ResponseEntity postAssignedDistributionSet(@PathVariable("filterId") final Long filterId, + @RequestBody final MgmtId dsId); + + /** + * Handles the DELETE request for removing the distribution set for auto + * assignment of a target filter. + * + * @param filterId + * of the target to change + * @return http status + */ + @RequestMapping(method = RequestMethod.DELETE, value = "/{filterId}/autoAssignDS") + ResponseEntity deleteAssignedDistributionSet(@PathVariable("filterId") final Long filterId); + +} diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResource.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResource.java index 721995d88..1da8d8a5f 100644 --- a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResource.java +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResource.java @@ -23,6 +23,7 @@ import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtTargetAssignmentR import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.MgmtSoftwareModule; import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.MgmtSoftwareModuleAssigment; import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTarget; +import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQuery; import org.eclipse.hawkbit.mgmt.rest.api.MgmtDistributionSetRestApi; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; import org.eclipse.hawkbit.repository.DeploymentManagement; @@ -32,12 +33,14 @@ import org.eclipse.hawkbit.repository.EntityFactory; import org.eclipse.hawkbit.repository.OffsetBasedPageRequest; import org.eclipse.hawkbit.repository.SoftwareManagement; import org.eclipse.hawkbit.repository.SystemManagement; +import org.eclipse.hawkbit.repository.TargetFilterQueryManagement; import org.eclipse.hawkbit.repository.TargetManagement; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSetMetadata; import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.Target; +import org.eclipse.hawkbit.repository.model.TargetFilterQuery; import org.eclipse.hawkbit.repository.model.TargetWithActionType; import org.eclipse.hawkbit.security.SystemSecurityContext; import org.slf4j.Logger; @@ -66,6 +69,9 @@ public class MgmtDistributionSetResource implements MgmtDistributionSetRestApi { @Autowired private TargetManagement targetManagement; + @Autowired + private TargetFilterQueryManagement targetFilterQueryManagement; + @Autowired private DeploymentManagement deployManagament; @@ -223,6 +229,37 @@ public class MgmtDistributionSetResource implements MgmtDistributionSetRestApi { HttpStatus.OK); } + @Override + public ResponseEntity> getAutoAssignTargetFilterQueries( + @PathVariable("distributionSetId") Long distributionSetId, + @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) int pagingOffsetParam, + @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) int pagingLimitParam, + @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) String sortParam, + @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) String rsqlParam) { + // check if distribution set exists otherwise throw exception + // immediately + DistributionSet distributionSet = findDistributionSetWithExceptionIfNotFound(distributionSetId); + + final int sanitizedOffsetParam = PagingUtility.sanitizeOffsetParam(pagingOffsetParam); + final int sanitizedLimitParam = PagingUtility.sanitizePageLimitParam(pagingLimitParam); + final Sort sorting = PagingUtility.sanitizeTargetSortParam(sortParam); + + final Pageable pageable = new OffsetBasedPageRequest(sanitizedOffsetParam, sanitizedLimitParam, sorting); + final Page targetFilterQueries; + if (rsqlParam != null) { + targetFilterQueries = this.targetFilterQueryManagement.findTargetFilterQueryByAutoAssignDS(pageable, + distributionSet, rsqlParam); + } else { + targetFilterQueries = this.targetFilterQueryManagement.findTargetFilterQueryByAutoAssignDS(pageable, + distributionSet); + } + + return new ResponseEntity<>( + new PagedList<>(MgmtTargetFilterQueryMapper.toResponse(targetFilterQueries.getContent()), + targetFilterQueries.getTotalElements()), + HttpStatus.OK); + } + @Override public ResponseEntity createAssignedTarget( @PathVariable("distributionSetId") final Long distributionSetId, diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryMapper.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryMapper.java new file mode 100644 index 000000000..629a1bc4e --- /dev/null +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryMapper.java @@ -0,0 +1,95 @@ +/** + * Copyright (c) 2015 Bosch Software Innovations 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 + */ +package org.eclipse.hawkbit.mgmt.rest.resource; + +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQuery; +import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQueryRequestBody; +import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetFilterQueryRestApi; +import org.eclipse.hawkbit.repository.EntityFactory; +import org.eclipse.hawkbit.repository.model.DistributionSet; +import org.eclipse.hawkbit.repository.model.TargetFilterQuery; + +/** + * A mapper which maps repository model to RESTful model representation and + * back. + * + */ +public final class MgmtTargetFilterQueryMapper { + + private MgmtTargetFilterQueryMapper() { + // Utility class + } + + /** + * Create a response for targets. + * + * @param filters + * list of targets + * @return the response + */ + public static List toResponse(final Iterable filters) { + final List mappedList = new ArrayList<>(); + if (filters != null) { + for (final TargetFilterQuery filter : filters) { + final MgmtTargetFilterQuery response = toResponse(filter); + mappedList.add(response); + } + } + return mappedList; + } + + /** + * Create a response for target. + * + * @param filter + * the target + * @return the response + */ + public static MgmtTargetFilterQuery toResponse(final TargetFilterQuery filter) { + if (filter == null) { + return null; + } + final MgmtTargetFilterQuery targetRest = new MgmtTargetFilterQuery(); + targetRest.setFilterId(filter.getId()); + targetRest.setName(filter.getName()); + targetRest.setQuery(filter.getQuery()); + + targetRest.setCreatedBy(filter.getCreatedBy()); + targetRest.setLastModifiedBy(filter.getLastModifiedBy()); + + targetRest.setCreatedAt(filter.getCreatedAt()); + targetRest.setLastModifiedAt(filter.getLastModifiedAt()); + + DistributionSet distributionSet = filter.getAutoAssignDistributionSet(); + if (distributionSet != null) { + targetRest.setAutoAssignDistributionSet(distributionSet.getId()); + } + + targetRest.add(linkTo(methodOn(MgmtTargetFilterQueryRestApi.class).getFilter(filter.getId())).withRel("self")); + targetRest.add(linkTo(methodOn(MgmtTargetFilterQueryRestApi.class).postAssignedDistributionSet(filter.getId(),null)).withRel("autoAssignDS")); + + return targetRest; + } + + static TargetFilterQuery fromRequest(final EntityFactory entityFactory, + final MgmtTargetFilterQueryRequestBody filterRest) { + final TargetFilterQuery filter = entityFactory.generateTargetFilterQuery(); + filter.setName(filterRest.getName()); + filter.setQuery(filterRest.getQuery()); + + return filter; + } + +} diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryResource.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryResource.java new file mode 100644 index 000000000..cee14cf2c --- /dev/null +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryResource.java @@ -0,0 +1,180 @@ +/** + * Copyright (c) 2015 Bosch Software Innovations 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 + */ +package org.eclipse.hawkbit.mgmt.rest.resource; + +import java.util.List; + +import org.eclipse.hawkbit.mgmt.json.model.MgmtId; +import org.eclipse.hawkbit.mgmt.json.model.PagedList; +import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSet; +import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQuery; +import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQueryRequestBody; +import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; +import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetFilterQueryRestApi; +import org.eclipse.hawkbit.repository.DistributionSetManagement; +import org.eclipse.hawkbit.repository.EntityFactory; +import org.eclipse.hawkbit.repository.OffsetBasedPageRequest; +import org.eclipse.hawkbit.repository.TargetFilterQueryManagement; +import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; +import org.eclipse.hawkbit.repository.model.DistributionSet; +import org.eclipse.hawkbit.repository.model.TargetFilterQuery; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; +import org.springframework.data.domain.Sort; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +/** + * REST Resource handling target CRUD operations. + */ +@RestController +public class MgmtTargetFilterQueryResource implements MgmtTargetFilterQueryRestApi { + private static final Logger LOG = LoggerFactory.getLogger(MgmtTargetFilterQueryResource.class); + + @Autowired + private TargetFilterQueryManagement filterManagement; + + @Autowired + private DistributionSetManagement distributionSetManagement; + + @Autowired + private EntityFactory entityFactory; + + @Override + public ResponseEntity getFilter(@PathVariable("filterId") Long filterId) { + final TargetFilterQuery findTarget = findFilterWithExceptionIfNotFound(filterId); + // to single response include poll status + final MgmtTargetFilterQuery response = MgmtTargetFilterQueryMapper.toResponse(findTarget); + + return new ResponseEntity<>(response, HttpStatus.OK); + } + + @Override + public ResponseEntity> getFilters( + @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) int pagingOffsetParam, + @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) int pagingLimitParam, + @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) String sortParam, + @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) String rsqlParam) { + + final int sanitizedOffsetParam = PagingUtility.sanitizeOffsetParam(pagingOffsetParam); + final int sanitizedLimitParam = PagingUtility.sanitizePageLimitParam(pagingLimitParam); + final Sort sorting = PagingUtility.sanitizeTargetSortParam(sortParam); + + final Pageable pageable = new OffsetBasedPageRequest(sanitizedOffsetParam, sanitizedLimitParam, sorting); + final Slice findTargetFiltersAll; + final Long countTargetsAll; + if (rsqlParam != null) { + final Page findFilterPage = this.filterManagement + .findTargetFilterQueryByFilter(pageable, rsqlParam); + countTargetsAll = findFilterPage.getTotalElements(); + findTargetFiltersAll = findFilterPage; + } else { + findTargetFiltersAll = this.filterManagement.findAllTargetFilterQuery(pageable); + countTargetsAll = this.filterManagement.countAllTargetFilterQuery(); + } + + final List rest = MgmtTargetFilterQueryMapper + .toResponse(findTargetFiltersAll.getContent()); + return new ResponseEntity<>(new PagedList(rest, countTargetsAll), HttpStatus.OK); + } + + @Override + public ResponseEntity createFilter(@RequestBody MgmtTargetFilterQueryRequestBody filter) { + final TargetFilterQuery createdTarget = this.filterManagement + .createTargetFilterQuery(MgmtTargetFilterQueryMapper.fromRequest(entityFactory, filter)); + + return new ResponseEntity<>(MgmtTargetFilterQueryMapper.toResponse(createdTarget), HttpStatus.CREATED); + } + + @Override + public ResponseEntity updateFilter(@PathVariable("filterId") Long filterId, + @RequestBody MgmtTargetFilterQueryRequestBody targetFilterRest) { + + final TargetFilterQuery existingFilter = findFilterWithExceptionIfNotFound(filterId); + LOG.debug("updating target filter query {}", existingFilter.getId()); + if (targetFilterRest.getName() != null) { + existingFilter.setName(targetFilterRest.getName()); + } + if (targetFilterRest.getQuery() != null) { + existingFilter.setQuery(targetFilterRest.getQuery()); + } + + final TargetFilterQuery updateFilter = this.filterManagement.updateTargetFilterQuery(existingFilter); + + return new ResponseEntity<>(MgmtTargetFilterQueryMapper.toResponse(updateFilter), HttpStatus.OK); + } + + @Override + public ResponseEntity deleteFilter(@PathVariable("filterId") Long filterId) { + final TargetFilterQuery filter = findFilterWithExceptionIfNotFound(filterId); + this.filterManagement.deleteTargetFilterQuery(filter.getId()); + LOG.debug("{} target filter query deleted, return status {}", filterId, HttpStatus.OK); + return new ResponseEntity<>(HttpStatus.OK); + } + + @Override + public ResponseEntity postAssignedDistributionSet(@PathVariable("filterId") Long filterId, + @RequestBody MgmtId dsId) { + final TargetFilterQuery filter = findFilterWithExceptionIfNotFound(filterId); + + DistributionSet distributionSet; + distributionSet = distributionSetManagement.findDistributionSetById(dsId.getId()); + if (distributionSet == null) { + throw new EntityNotFoundException("DistributionSet with Id {" + dsId + "} does not exist"); + } + + filter.setAutoAssignDistributionSet(distributionSet); + + final TargetFilterQuery updateFilter = this.filterManagement.updateTargetFilterQuery(filter); + + return new ResponseEntity<>(MgmtTargetFilterQueryMapper.toResponse(updateFilter), HttpStatus.OK); + } + + @Override + public ResponseEntity getAssignedDistributionSet(@PathVariable("filterId") Long filterId) { + final TargetFilterQuery filter = findFilterWithExceptionIfNotFound(filterId); + DistributionSet autoAssignDistributionSet = filter.getAutoAssignDistributionSet(); + MgmtDistributionSet distributionSetRest = MgmtDistributionSetMapper.toResponse(autoAssignDistributionSet); + final HttpStatus retStatus; + if (distributionSetRest == null) { + retStatus = HttpStatus.NO_CONTENT; + } else { + retStatus = HttpStatus.OK; + } + return new ResponseEntity<>(distributionSetRest, retStatus); + } + + @Override + public ResponseEntity deleteAssignedDistributionSet(@PathVariable("filterId") Long filterId) { + final TargetFilterQuery filter = findFilterWithExceptionIfNotFound(filterId); + + filter.setAutoAssignDistributionSet(null); + + this.filterManagement.updateTargetFilterQuery(filter); + + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + private TargetFilterQuery findFilterWithExceptionIfNotFound(final Long filterId) { + final TargetFilterQuery filter = this.filterManagement.findTargetFilterQueryById(filterId); + if (filter == null) { + throw new EntityNotFoundException("TargetFilterQuery with Id {" + filterId + "} does not exist"); + } + return filter; + } + +} diff --git a/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResourceTest.java b/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResourceTest.java index 725a10538..d59c114e5 100644 --- a/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResourceTest.java +++ b/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResourceTest.java @@ -30,11 +30,8 @@ import java.util.Set; import org.apache.commons.lang3.RandomStringUtils; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; +import org.eclipse.hawkbit.repository.model.*; import org.eclipse.hawkbit.repository.model.Action.Status; -import org.eclipse.hawkbit.repository.model.DistributionSet; -import org.eclipse.hawkbit.repository.model.DistributionSetMetadata; -import org.eclipse.hawkbit.repository.model.SoftwareModule; -import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.test.util.TestdataFactory; import org.eclipse.hawkbit.repository.test.util.WithUser; import org.eclipse.hawkbit.rest.AbstractRestIntegrationTest; @@ -297,6 +294,25 @@ public class MgmtDistributionSetResourceTest extends AbstractRestIntegrationTest .andExpect(jsonPath("$.content[0].controllerId", equalTo(knownTargetId))); } + @Test + @Description("Ensures that target filters with auto assign DS are returned as persisted in the repository.") + public void getAutoAssignTargetFiltersOfDistributionSet() throws Exception { + // prepare distribution set + final String knownFilterName = "a"; + final Set createDistributionSetsAlphabetical = createDistributionSetsAlphabetical(1); + final DistributionSet createdDs = createDistributionSetsAlphabetical.iterator().next(); + + final TargetFilterQuery tfq = targetFilterQueryManagement + .createTargetFilterQuery(entityFactory.generateTargetFilterQuery(knownFilterName, "x==y", createdDs)); + // create some dummy targets which are not assigned or installed + targetFilterQueryManagement.createTargetFilterQuery(entityFactory.generateTargetFilterQuery("b", "x==y")); + targetFilterQueryManagement.createTargetFilterQuery(entityFactory.generateTargetFilterQuery("c", "x==y")); + + mvc.perform(get(MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING + "/" + createdDs.getId() + + "/autoAssignTargetFilters")).andExpect(status().isOk()).andExpect(jsonPath("$.size", equalTo(1))) + .andExpect(jsonPath("$.content[0].name", equalTo(knownFilterName))); + } + @Test @Description("Ensures that DS in repository are listed with proper paging properties.") public void getDistributionSetsWithoutAddtionalRequestParameters() throws Exception { diff --git a/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryResourceTest.java b/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryResourceTest.java new file mode 100644 index 000000000..5694c9bfb --- /dev/null +++ b/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryResourceTest.java @@ -0,0 +1,362 @@ +/** + * Copyright (c) 2015 Bosch Software Innovations 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 + */ +package org.eclipse.hawkbit.mgmt.rest.resource; + +import static com.google.common.collect.Lists.newArrayList; +import static org.fest.assertions.api.Assertions.assertThat; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.Matchers.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.util.*; +import java.util.stream.Collectors; + +import org.eclipse.hawkbit.exception.SpServerError; +import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; +import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException; +import org.eclipse.hawkbit.repository.model.*; +import org.eclipse.hawkbit.repository.model.Action.ActionType; +import org.eclipse.hawkbit.rest.AbstractRestIntegrationTest; +import org.eclipse.hawkbit.rest.exception.MessageNotReadableException; +import org.eclipse.hawkbit.rest.json.model.ExceptionInfo; +import org.eclipse.hawkbit.rest.util.JsonBuilder; +import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter; +import org.json.JSONObject; +import org.junit.Test; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Slice; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MvcResult; + +import com.jayway.jsonpath.JsonPath; + +import ru.yandex.qatools.allure.annotations.Description; +import ru.yandex.qatools.allure.annotations.Features; +import ru.yandex.qatools.allure.annotations.Stories; + +/** + * Spring MVC Tests against the MgmtTargetResource. + * + */ +@Features("Component Tests - Management API") +@Stories("Target Filter Query Resource") +public class MgmtTargetFilterQueryResourceTest extends AbstractRestIntegrationTest { + + private static final String TARGET_DESCRIPTION_TEST = "created in test"; + + private static final String JSON_PATH_ROOT = "$"; + + // fields, attributes + private static final String JSON_PATH_FIELD_ID = ".id"; + private static final String JSON_PATH_FIELD_NAME = ".name"; + private static final String JSON_PATH_FIELD_QUERY = ".query"; + private static final String JSON_PATH_FIELD_CONTENT = ".content"; + private static final String JSON_PATH_FIELD_SIZE = ".size"; + private static final String JSON_PATH_FIELD_TOTAL = ".total"; + private static final String JSON_PATH_FIELD_AUTO_ASSIGN_DS = ".autoAssignDistributionSet"; + + // target + // $.field + static final String JSON_PATH_PAGED_LIST_CONTENT = JSON_PATH_ROOT + JSON_PATH_FIELD_CONTENT; + static final String JSON_PATH_PAGED_LIST_SIZE = JSON_PATH_ROOT + JSON_PATH_FIELD_SIZE; + static final String JSON_PATH_PAGED_LIST_TOTAL = JSON_PATH_ROOT + JSON_PATH_FIELD_TOTAL; + + private static final String JSON_PATH_NAME = JSON_PATH_ROOT + JSON_PATH_FIELD_NAME; + private static final String JSON_PATH_ID = JSON_PATH_ROOT + JSON_PATH_FIELD_ID; + private static final String JSON_PATH_QUERY = JSON_PATH_ROOT + JSON_PATH_FIELD_QUERY; + private static final String JSON_PATH_AUTO_ASSIGN_DS = JSON_PATH_ROOT + JSON_PATH_FIELD_AUTO_ASSIGN_DS; + + + @Test + @Description("Ensures that deletion is executed if permitted.") + public void deleteTargetFilterQueryReturnsOK() throws Exception { + final String filterName = "filter_01"; + TargetFilterQuery filterQuery = createSingleTargetFilterQuery(filterName, "name=test_01"); + + mvc.perform(delete(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + filterQuery.getId())) + .andExpect(status().isOk()); + + TargetFilterQuery tfq = targetFilterQueryManagement.findTargetFilterQueryById(filterQuery.getId()); + assertThat(tfq).isNull(); + } + + @Test + @Description("Ensures that deletion is refused with not found if target does not exist.") + public void deleteTargetWhichDoesNotExistsLeadsToEntityNotFound() throws Exception { + final String notExistingId = "4395"; + + mvc.perform(delete(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + notExistingId)) + .andExpect(status().isNotFound()); + } + + @Test + @Description("Ensures that update is refused with not found if target does not exist.") + public void updateTargetWhichDoesNotExistsLeadsToEntityNotFound() throws Exception { + final String notExistingId = "4395"; + mvc.perform(put(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + notExistingId).content("{}") + .contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()) + .andExpect(status().isNotFound()); + } + + @Test + @Description("Ensures that update request is reflected by repository.") + public void updateTargetFilterQueryQuery() throws Exception { + final String filterName = "filter_02"; + final String filterQuery = "name=test_02"; + final String filterQuery2 = "name=test_02_changed"; + final String body = new JSONObject().put("query", filterQuery2).toString(); + + // prepare + TargetFilterQuery tfq = createSingleTargetFilterQuery(filterName, filterQuery); + + mvc.perform(put(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId()).content(body) + .contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) + .andExpect(jsonPath("$.id", equalTo(tfq.getId().intValue()))) + .andExpect(jsonPath("$.query", equalTo(filterQuery2))) + .andExpect(jsonPath("$.name", equalTo(filterName))); + + TargetFilterQuery tfqCheck = targetFilterQueryManagement.findTargetFilterQueryById(tfq.getId()); + assertThat(tfqCheck.getQuery()).isEqualTo(filterQuery2); + assertThat(tfqCheck.getName()).isEqualTo(filterName); + } + + @Test + @Description("Ensures that update request is reflected by repository.") + public void updateTargetFilterQueryName() throws Exception { + final String filterName = "filter_03"; + final String filterName2 = "filter_03_changed"; + final String filterQuery = "name=test_03"; + final String body = new JSONObject().put("name", filterName2).toString(); + + // prepare + TargetFilterQuery tfq = entityFactory.generateTargetFilterQuery(); + tfq.setName(filterName); + tfq.setQuery(filterQuery); + tfq = targetFilterQueryManagement.createTargetFilterQuery(tfq); + + mvc.perform(put(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId()).content(body) + .contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) + .andExpect(jsonPath("$.id", equalTo(tfq.getId().intValue()))) + .andExpect(jsonPath("$.query", equalTo(filterQuery))) + .andExpect(jsonPath("$.name", equalTo(filterName2))); + + TargetFilterQuery tfqCheck = targetFilterQueryManagement.findTargetFilterQueryById(tfq.getId()); + assertThat(tfqCheck.getQuery()).isEqualTo(filterQuery); + assertThat(tfqCheck.getName()).isEqualTo(filterName2); + } + + + @Test + @Description("Ensures that request returns list of filters in defined format.") + public void getTargetFilterQueryWithoutAdditionalRequestParameters() throws Exception { + final int knownTargetAmount = 3; + final String idA = "a"; + final String idB = "b"; + final String idC = "c"; + final String testQuery = "name=test"; + + createSingleTargetFilterQuery(idA, testQuery); + createSingleTargetFilterQuery(idB, testQuery); + createSingleTargetFilterQuery(idC, testQuery); + + mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING)).andExpect(status().isOk()) + .andDo(MockMvcResultPrinter.print()) + .andExpect(jsonPath(JSON_PATH_PAGED_LIST_TOTAL, equalTo(knownTargetAmount))) + .andExpect(jsonPath(JSON_PATH_PAGED_LIST_SIZE, equalTo(knownTargetAmount))) + .andExpect(jsonPath(JSON_PATH_PAGED_LIST_CONTENT, hasSize(knownTargetAmount))) + // idA + .andExpect(jsonPath("$.content.[?(@.name==" + idA + ")].name", contains(idA))) + .andExpect(jsonPath("$.content.[?(@.name==" + idA + ")].query", contains(testQuery))) + // idB + .andExpect(jsonPath("$.content.[?(@.name==" + idB + ")].name", contains(idB))) + .andExpect(jsonPath("$.content.[?(@.name==" + idB + ")].query", contains(testQuery))) + // idC + .andExpect(jsonPath("$.content.[?(@.name==" + idC + ")].name", contains(idC))) + .andExpect(jsonPath("$.content.[?(@.name==" + idC + ")].query", contains(testQuery))); + } + + @Test + @Description("Ensures that request returns list of filters in defined format in size reduced by given limit parameter.") + public void getTargetWithPagingLimitRequestParameter() throws Exception { + final int limitSize = 1; + final int knownTargetAmount = 3; + final String idA = "a"; + final String idB = "b"; + final String idC = "c"; + final String testQuery = "name=test"; + + createSingleTargetFilterQuery(idA, testQuery); + createSingleTargetFilterQuery(idB, testQuery); + createSingleTargetFilterQuery(idC, testQuery); + + mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING) + .param(MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, String.valueOf(limitSize))) + .andExpect(status().isOk()).andDo(MockMvcResultPrinter.print()) + .andExpect(jsonPath(JSON_PATH_PAGED_LIST_TOTAL, equalTo(knownTargetAmount))) + .andExpect(jsonPath(JSON_PATH_PAGED_LIST_SIZE, equalTo(limitSize))) + .andExpect(jsonPath(JSON_PATH_PAGED_LIST_CONTENT, hasSize(limitSize))) + // idA + .andExpect(jsonPath("$.content.[?(@.name==" + idA + ")].name", contains(idA))) + .andExpect(jsonPath("$.content.[?(@.name==" + idA + ")].query", contains(testQuery))); + } + + @Test + @Description("Ensures that request returns list of filters in defined format in size reduced by given limit and offset parameter.") + public void getTargetWithPagingLimitAndOffsetRequestParameter() throws Exception { + final int knownTargetAmount = 5; + final int offsetParam = 2; + final int expectedSize = knownTargetAmount - offsetParam; + final String idC = "c"; + final String idD = "d"; + final String idE = "e"; + final String testQuery = "name=test"; + + createSingleTargetFilterQuery("a", testQuery); + createSingleTargetFilterQuery("b", testQuery); + createSingleTargetFilterQuery(idC, testQuery); + createSingleTargetFilterQuery(idD, testQuery); + createSingleTargetFilterQuery(idE, testQuery); + + mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING) + .param(MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, String.valueOf(offsetParam)) + .param(MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, String.valueOf(knownTargetAmount))) + .andExpect(status().isOk()).andDo(MockMvcResultPrinter.print()) + .andExpect(jsonPath(JSON_PATH_PAGED_LIST_TOTAL, equalTo(knownTargetAmount))) + .andExpect(jsonPath(JSON_PATH_PAGED_LIST_SIZE, equalTo(expectedSize))) + .andExpect(jsonPath(JSON_PATH_PAGED_LIST_CONTENT, hasSize(expectedSize))) + // idA + .andExpect(jsonPath("$.content.[?(@.name==" + idC + ")].name", contains(idC))) + .andExpect(jsonPath("$.content.[?(@.name==" + idC + ")].query", contains(testQuery))) + // idB + .andExpect(jsonPath("$.content.[?(@.name==" + idD + ")].name", contains(idD))) + .andExpect(jsonPath("$.content.[?(@.name==" + idD + ")].query", contains(testQuery))) + // idC + .andExpect(jsonPath("$.content.[?(@.name==" + idE + ")].name", contains(idE))) + .andExpect(jsonPath("$.content.[?(@.name==" + idE + ")].query", contains(testQuery))); + } + + @Test + public void getSingleTarget() throws Exception { + // create first a target which can be retrieved by rest interface + final String knownQuery = "name=test01"; + final String knownName = "someName"; + final TargetFilterQuery tfq = createSingleTargetFilterQuery(knownName, knownQuery); + final String hrefPrefix = "http://localhost"+MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING+"/" + tfq.getId(); + // test + mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId())) + .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) + .andExpect(jsonPath(JSON_PATH_NAME, equalTo(knownName))) + .andExpect(jsonPath(JSON_PATH_QUERY, equalTo(knownQuery))) + .andExpect(jsonPath("$._links.self.href", equalTo(hrefPrefix))) + .andExpect(jsonPath("$._links.autoAssignDS.href", equalTo(hrefPrefix + "/autoAssignDS"))); + } + + @Test + public void getSingleTargetNoExistsResponseNotFound() throws Exception { + final String targetIdNotExists = "546546"; + // test + + final MvcResult mvcResult = mvc + .perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + targetIdNotExists)) + .andExpect(status().isNotFound()).andReturn(); + + // verify response json exception message + final ExceptionInfo exceptionInfo = ResourceUtility + .convertException(mvcResult.getResponse().getContentAsString()); + assertThat(exceptionInfo.getErrorCode()).isEqualTo(SpServerError.SP_REPO_ENTITY_NOT_EXISTS.getKey()); + } + + @Test + public void createTargetFilterQueryWithBadPayloadBadRequest() throws Exception { + final String notJson = "abc"; + + final MvcResult mvcResult = mvc + .perform(post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING).content(notJson) + .contentType(MediaType.APPLICATION_JSON)) + .andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest()).andReturn(); + + assertThat(targetFilterQueryManagement.countAllTargetFilterQuery()).isEqualTo(0); + + // verify response json exception message + final ExceptionInfo exceptionInfo = ResourceUtility + .convertException(mvcResult.getResponse().getContentAsString()); + assertThat(exceptionInfo.getExceptionClass()).isEqualTo(MessageNotReadableException.class.getName()); + assertThat(exceptionInfo.getErrorCode()).isEqualTo(SpServerError.SP_REST_BODY_NOT_READABLE.getKey()); + } + + @Test + public void setAutoAssignDistributionSetToTargetFilterQuery() throws Exception { + + final String knownQuery = "name=test05"; + final String knownName = "filter05"; + + final DistributionSet set = testdataFactory.createDistributionSet("one"); + final TargetFilterQuery tfq = createSingleTargetFilterQuery(knownName, knownQuery); + + mvc.perform(post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/"+tfq.getId()+"/autoAssignDS") + .content("{\"id\":" + set.getId() + "}").contentType(MediaType.APPLICATION_JSON)) + .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()); + + assertThat(targetFilterQueryManagement.findTargetFilterQueryById(tfq.getId()).getAutoAssignDistributionSet()).isEqualTo(set); + + final String hrefPrefix = "http://localhost"+MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING+"/" + tfq.getId(); + + mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId())) + .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) + .andExpect(jsonPath(JSON_PATH_NAME, equalTo(knownName))) + .andExpect(jsonPath(JSON_PATH_QUERY, equalTo(knownQuery))) + .andExpect(jsonPath(JSON_PATH_AUTO_ASSIGN_DS, equalTo(set.getId().intValue()))) + .andExpect(jsonPath("$._links.self.href", equalTo(hrefPrefix))) + .andExpect(jsonPath("$._links.autoAssignDS.href", equalTo(hrefPrefix + "/autoAssignDS"))); + } + + @Test + public void deleteAutoAssignDistributionSetOfTargetFilterQuery() throws Exception { + + final String knownQuery = "name=test06"; + final String knownName = "filter06"; + final String dsName = "testDS"; + + final DistributionSet set = testdataFactory.createDistributionSet(dsName); + TargetFilterQuery tfq = entityFactory.generateTargetFilterQuery(); + tfq.setName(knownName); + tfq.setQuery(knownQuery); + tfq.setAutoAssignDistributionSet(set); + tfq = targetFilterQueryManagement.createTargetFilterQuery(tfq); + + assertThat(targetFilterQueryManagement.findTargetFilterQueryById(tfq.getId()).getAutoAssignDistributionSet()) + .isEqualTo(set); + + mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/"+tfq.getId()+"/autoAssignDS")) + .andExpect(status().isOk()) + .andExpect(jsonPath(JSON_PATH_NAME, equalTo(dsName))); + + mvc.perform(delete(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/"+tfq.getId()+"/autoAssignDS")) + .andExpect(status().isNoContent()); + + assertThat(targetFilterQueryManagement.findTargetFilterQueryById(tfq.getId()).getAutoAssignDistributionSet()) + .isNull(); + + mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/"+tfq.getId()+"/autoAssignDS")) + .andExpect(status().isNoContent()); + + + } + + private TargetFilterQuery createSingleTargetFilterQuery(final String name, final String query) { + final TargetFilterQuery target = entityFactory.generateTargetFilterQuery(); + target.setName(name); + target.setQuery(query); + return targetFilterQueryManagement.createTargetFilterQuery(target); + } + +} diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/AutoAssignProperties.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/AutoAssignProperties.java new file mode 100644 index 000000000..9f6c85c00 --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/AutoAssignProperties.java @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2015 Bosch Software Innovations 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 + */ +package org.eclipse.hawkbit.repository; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * Rollout Management properties. + * + */ +@Component +@ConfigurationProperties("hawkbit.autoassign") +public class AutoAssignProperties { + /** + * Autoassign scheduler configuration. + */ + public static class Scheduler { + // used by @Scheduled annotation which needs constant + public static final String PROP_SCHEDULER_DELAY_PLACEHOLDER = "${hawkbit.autoassign.scheduler.fixedDelay:60000}"; + + /** + * Schedule where the autoassign scheduler looks necessary state changes in + * milliseconds. + */ + private long fixedDelay = 60000L; + + public long getFixedDelay() { + return fixedDelay; + } + + public void setFixedDelay(final long fixedDelay) { + this.fixedDelay = fixedDelay; + } + + } + + private final Scheduler scheduler = new Scheduler(); + + public Scheduler getScheduler() { + return scheduler; + } + +} diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java index 270de3e74..ab1a680ba 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java @@ -104,6 +104,27 @@ public interface DeploymentManagement { DistributionSetAssignmentResult assignDistributionSet(@NotNull Long dsID, @NotEmpty Collection targets); + /** + * method assigns the {@link DistributionSet} to all {@link Target}s by + * their IDs with a specific {@link ActionType} and an action message. + * + * @param dsID + * the ID of the distribution set to assign + * @param targets + * a list of all targets and their action type + * @param actionMessage + * an optional message for the action status + * @return the assignment result + * + * @throw IncompleteDistributionSetException if mandatory + * {@link SoftwareModuleType} are not assigned as define by the + * {@link DistributionSetType}. + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET) + DistributionSetAssignmentResult assignDistributionSet(@NotNull Long dsID, + @NotEmpty Collection targets, + String actionMessage); + /** * method assigns the {@link DistributionSet} to all {@link Target}s by * their IDs with a specific {@link ActionType} and {@code forcetime}. @@ -364,9 +385,6 @@ public interface DeploymentManagement { * pagination parameter * @param action * to be filtered on - * @param withMessages - * to true if {@link ActionStatus#getMessages()} - * need to be fetched. * @return the corresponding {@link Page} of {@link ActionStatus} */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) @@ -463,8 +481,6 @@ public interface DeploymentManagement { * * @param action * to be canceled - * @param target - * for which the action needs cancellation * * @return generated {@link Action} or null if not active on * {@link Target}. @@ -478,8 +494,6 @@ public interface DeploymentManagement { * Updates a {@link Action} and forces the {@link Action} if it's not * already forced. * - * @param targetId - * the ID of the target * @param actionId * the ID of the action * @return the updated or the found {@link Action} diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/EntityFactory.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/EntityFactory.java index 87919729e..0491eac0c 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/EntityFactory.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/EntityFactory.java @@ -316,6 +316,30 @@ public interface EntityFactory { */ TargetFilterQuery generateTargetFilterQuery(); + /** + * Generates an {@link TargetFilterQuery} without persisting it. + * + * @param name + * name for the filter + * @param query + * query of the filter + * @return {@link TargetFilterQuery} object + */ + TargetFilterQuery generateTargetFilterQuery(String name, String query); + + /** + * Generates an {@link TargetFilterQuery} without persisting it. + * + * @param name + * name for the filter + * @param query + * query of the filter + * @param autoAssignDS + * auto assign distribution set + * @return {@link TargetFilterQuery} object + */ + TargetFilterQuery generateTargetFilterQuery(String name, String query, DistributionSet autoAssignDS); + /** * Generates an empty {@link TargetTag} without persisting it. * diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetFilterQueryManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetFilterQueryManagement.java index d748ed470..bd7bec4e5 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetFilterQueryManagement.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetFilterQueryManagement.java @@ -13,6 +13,7 @@ import javax.validation.constraints.NotNull; import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions; import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException; import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException; +import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.TargetFilterQuery; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -70,6 +71,13 @@ public interface TargetFilterQueryManagement { @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) Page findAllTargetFilterQuery(@NotNull Pageable pageable); + /** + * Counts all target filter queries + * @return the number of all target filter queries + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) + Long countAllTargetFilterQuery(); + /** * Retrieves all target filter query which {@link TargetFilterQuery}. * @@ -77,11 +85,64 @@ public interface TargetFilterQueryManagement { * @param pageable * pagination parameter * @param name - * target filter query name + * name filter * @return the page with the found {@link TargetFilterQuery} */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) - Page findTargetFilterQueryByFilters(@NotNull Pageable pageable, String name); + Page findTargetFilterQueryByName(@NotNull Pageable pageable, String name); + + /** + * Retrieves all target filter query which {@link TargetFilterQuery}. + * + * + * @param pageable + * pagination parameter + * @param rsqlFilter + * RSQL filter string + * @return the page with the found {@link TargetFilterQuery} + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) + Page findTargetFilterQueryByFilter(@NotNull Pageable pageable, String rsqlFilter); + + /** + * Retrieves all target filter query which {@link TargetFilterQuery}. + * + * + * @param pageable + * pagination parameter + * @param distributionSet + * the auto assign distribution set + * @return the page with the found {@link TargetFilterQuery} + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) + Page findTargetFilterQueryByAutoAssignDS(@NotNull Pageable pageable, + DistributionSet distributionSet); + + /** + * Retrieves all target filter query which {@link TargetFilterQuery}. + * + * + * @param pageable + * pagination parameter + * @param distributionSet + * the auto assign distribution set + * @param rsqlParam + * RSQL filter + * @return the page with the found {@link TargetFilterQuery} + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) + Page findTargetFilterQueryByAutoAssignDS(@NotNull Pageable pageable, + DistributionSet distributionSet, String rsqlParam); + + /** + * Retrieves all target filter query with auto assign DS which {@link TargetFilterQuery}. + * + * + * @return the page with the found {@link TargetFilterQuery} + * @param pageable + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) + Page findTargetFilterQueryWithAutoAssignDS(@NotNull Pageable pageable); /** * Find target filter query by id. 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 5fdfb318e..d8d9e3b20 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 @@ -242,6 +242,35 @@ public interface TargetManagement { List findAllTargetIdsByTargetFilterQuery(@NotNull Pageable pageRequest, @NotNull TargetFilterQuery targetFilterQuery); + /** + * Finds all targets for all the given parameter {@link TargetFilterQuery} + * and that don't have the specified assigned distribution set. + * and returns not the full target but {@link TargetIdName}. + * + * @param pageRequest + * the pageRequest to enhance the query for paging and sorting + * @param targetFilterQuery + * {@link TargetFilterQuery} + * @return the found {@link TargetIdName}s + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) + Page findAllTargetIdsByTargetFilterQueryAndNonDS(@NotNull Pageable pageRequest, + Long distributionSetId, + @NotNull TargetFilterQuery targetFilterQuery); + + /** + * Counts all targets for all the given parameter {@link TargetFilterQuery} + * and that don't have the specified assigned distribution set. and returns + * not the full target but {@link TargetIdName}. + * + * @param targetFilterQuery + * {@link TargetFilterQuery} + * @return the found {@link TargetIdName}s + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) + Long countTargetByTargetFilterQueryAndNonDS(Long distributionSetId, + @NotNull TargetFilterQuery targetFilterQuery); + /** * retrieves {@link Target}s by the assigned {@link DistributionSet} without * details, i.e. NO {@link Target#getTags()} and {@link Target#getActions()} diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSet.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSet.java index 5747b38f3..9284ce5cf 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSet.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSet.java @@ -73,6 +73,11 @@ public interface DistributionSet extends NamedVersionedEntity { */ List getAssignedTargets(); + /** + * @return the auto assign target filters + */ + List getAutoAssignFilters(); + /** * @return the installedTargets */ diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetFilterQuery.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetFilterQuery.java index 5052a0060..5db7593a1 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetFilterQuery.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetFilterQuery.java @@ -56,4 +56,16 @@ public interface TargetFilterQuery extends TenantAwareBaseEntity { */ void setQuery(String query); + /** + * @return the auto assign {@link DistributionSet} if given. + */ + DistributionSet getAutoAssignDistributionSet(); + + /** + * @param distributionSet + * the {@link DistributionSet} that should be assigned to a + * target when this filter matches. + */ + void setAutoAssignDistributionSet(DistributionSet distributionSet); + } 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 5a2c6fdda..68560724b 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 @@ -175,13 +175,23 @@ public class JpaDeploymentManagement implements DeploymentManagement { @CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true) public DistributionSetAssignmentResult assignDistributionSet(final Long dsID, final Collection targets) { + return assignDistributionSet(dsID, targets, null); + } + + @Override + @Modifying + @Transactional(isolation = Isolation.READ_COMMITTED) + @CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true) + public DistributionSetAssignmentResult assignDistributionSet(final Long dsID, + final Collection targets, + final String actionMessage) { final JpaDistributionSet set = distributoinSetRepository.findOne(dsID); if (set == null) { throw new EntityNotFoundException( String.format("no %s with id %d found", DistributionSet.class.getSimpleName(), dsID)); } - return assignDistributionSetToTargets(set, targets, null, null); + return assignDistributionSetToTargets(set, targets, null, null, actionMessage); } @Override @@ -196,21 +206,23 @@ public class JpaDeploymentManagement implements DeploymentManagement { String.format("no %s with id %d found", DistributionSet.class.getSimpleName(), dsID)); } - return assignDistributionSetToTargets(set, targets, (JpaRollout) rollout, (JpaRolloutGroup) rolloutGroup); + return assignDistributionSetToTargets(set, targets, (JpaRollout) rollout, (JpaRolloutGroup) rolloutGroup, null); } /** * method assigns the {@link DistributionSet} to all {@link Target}s by * their IDs with a specific {@link ActionType} and {@code forcetime}. * - * @param dsID + * @param set * the ID of the distribution set to assign - * @param targets + * @param targetsWithActionType * a list of all targets and their action type * @param rollout * the rollout for this assignment * @param rolloutGroup * the rollout group for this assignment + * @param actionMessage + * an optional message to be written into the action status * @return the assignment result * * @throw IncompleteDistributionSetException if mandatory @@ -219,7 +231,7 @@ public class JpaDeploymentManagement implements DeploymentManagement { */ private DistributionSetAssignmentResult assignDistributionSetToTargets(@NotNull final JpaDistributionSet set, final Collection targetsWithActionType, final JpaRollout rollout, - final JpaRolloutGroup rolloutGroup) { + final JpaRolloutGroup rolloutGroup, final String actionMessage) { if (!set.isComplete()) { throw new IncompleteDistributionSetException( @@ -292,6 +304,9 @@ public class JpaDeploymentManagement implements DeploymentManagement { actionStatus.setAction(action); actionStatus.setOccurredAt(action.getCreatedAt()); actionStatus.setStatus(Status.RUNNING); + if(actionMessage != null) { + actionStatus.addMessage(actionMessage); + } actionStatusRepository.save(actionStatus); }); @@ -346,7 +361,7 @@ public class JpaDeploymentManagement implements DeploymentManagement { * the Target which has been assigned to a distribution set * @param actionId * the action id of the assignment - * @param softwareModules + * @param modules * the software modules which have been assigned */ private void assignDistributionSetEvent(final JpaTarget target, final Long actionId, @@ -364,11 +379,11 @@ public class JpaDeploymentManagement implements DeploymentManagement { } /** - * Removes {@link UpdateAction}s that are no longer necessary and sends + * Removes {@link Action}s that are no longer necessary and sends * cancellations to the controller. * - * @param myTarget - * to override {@link UpdateAction}s + * @param targetsIds + * to override {@link Action}s */ private Set overrideObsoleteUpdateActions(final List targetsIds) { @@ -403,7 +418,7 @@ public class JpaDeploymentManagement implements DeploymentManagement { return assignDistributionSetToTargets(set, tIDs.stream() .map(t -> new TargetWithActionType(t, actionType, forcedTime)).collect(Collectors.toList()), null, - null); + null, null); } @Override diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetManagement.java index e170ccad7..0459e50ac 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetManagement.java @@ -95,6 +95,9 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { @Autowired private DistributionSetMetadataRepository distributionSetMetadataRepository; + @Autowired + private TargetFilterQueryRepository targetFilterQueryRepository; + @Autowired private ActionRepository actionRepository; @@ -182,7 +185,9 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { // soft delete assigned if (!assigned.isEmpty()) { - distributionSetRepository.deleteDistributionSet(assigned.toArray(new Long[assigned.size()])); + Long[] dsIds = assigned.toArray(new Long[assigned.size()]); + distributionSetRepository.deleteDistributionSet(dsIds); + targetFilterQueryRepository.unsetAutoAssignDistributionSet(dsIds); } // mark the rest as hard delete diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaEntityFactory.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaEntityFactory.java index 51b59f08f..8079859f2 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaEntityFactory.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaEntityFactory.java @@ -132,6 +132,16 @@ public class JpaEntityFactory implements EntityFactory { return new JpaTargetFilterQuery(); } + @Override + public TargetFilterQuery generateTargetFilterQuery(String name, String query) { + return new JpaTargetFilterQuery(name, query); + } + + @Override + public TargetFilterQuery generateTargetFilterQuery(String name, String query, DistributionSet autoAssignDS) { + return new JpaTargetFilterQuery(name, query, (JpaDistributionSet) autoAssignDS); + } + @Override public SoftwareModuleType generateSoftwareModuleType() { return new JpaSoftwareModuleType(); 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 3b7f77b47..10361eaea 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 @@ -56,6 +56,9 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst @Autowired private TargetRepository targetRepository; + @Autowired + private TargetFilterQueryRepository targetFilterQueryRepository; + @Autowired private ActionRepository actionRepository; @@ -221,6 +224,7 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst tenantMetaDataRepository.deleteByTenantIgnoreCase(tenant); tenantConfigurationRepository.deleteByTenantIgnoreCase(tenant); targetRepository.deleteByTenantIgnoreCase(tenant); + targetFilterQueryRepository.deleteByTenantIgnoreCase(tenant); actionRepository.deleteByTenantIgnoreCase(tenant); rolloutGroupRepository.deleteByTenantIgnoreCase(tenant); rolloutRepository.deleteByTenantIgnoreCase(tenant); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetFilterQueryManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetFilterQueryManagement.java index 6438e19f6..27f803a00 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetFilterQueryManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetFilterQueryManagement.java @@ -12,12 +12,14 @@ import java.util.ArrayList; import java.util.List; import org.eclipse.hawkbit.repository.TargetFields; +import org.eclipse.hawkbit.repository.TargetFilterQueryFields; import org.eclipse.hawkbit.repository.TargetFilterQueryManagement; import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException; import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery; import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility; import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder; import org.eclipse.hawkbit.repository.jpa.specifications.TargetFilterQuerySpecification; +import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.TargetFilterQuery; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; @@ -33,6 +35,8 @@ import org.springframework.validation.annotation.Validated; import com.google.common.base.Strings; +import javax.validation.constraints.NotNull; + /** * JPA implementation of {@link TargetFilterQueryManagement}. * @@ -67,13 +71,18 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme return convertPage(targetFilterQueryRepository.findAll(pageable), pageable); } + @Override + public Long countAllTargetFilterQuery() { + return targetFilterQueryRepository.count(); + } + private static Page convertPage(final Page findAll, final Pageable pageable) { return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements()); } @Override - public Page findTargetFilterQueryByFilters(final Pageable pageable, final String name) { + public Page findTargetFilterQueryByName(final Pageable pageable, final String name) { final List> specList = new ArrayList<>(); if (!Strings.isNullOrEmpty(name)) { specList.add(TargetFilterQuerySpecification.likeName(name)); @@ -81,6 +90,41 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme return convertPage(findTargetFilterQueryByCriteriaAPI(pageable, specList), pageable); } + @Override + public Page findTargetFilterQueryByFilter(@NotNull Pageable pageable, String rsqlFilter) { + final List> specList = new ArrayList<>(); + if (!Strings.isNullOrEmpty(rsqlFilter)) { + specList.add(RSQLUtility.parse(rsqlFilter, TargetFilterQueryFields.class)); + } + return convertPage(findTargetFilterQueryByCriteriaAPI(pageable, specList), pageable); + } + + @Override + public Page findTargetFilterQueryByAutoAssignDS(@NotNull Pageable pageable, + DistributionSet distributionSet) { + return findTargetFilterQueryByAutoAssignDS(pageable, distributionSet, null); + } + + @Override + public Page findTargetFilterQueryByAutoAssignDS(@NotNull Pageable pageable, + DistributionSet distributionSet, String rsqlFilter) { + final List> specList = new ArrayList<>(); + if (distributionSet != null) { + specList.add(TargetFilterQuerySpecification.byAutoAssignDS(distributionSet)); + } + if (!Strings.isNullOrEmpty(rsqlFilter)) { + specList.add(RSQLUtility.parse(rsqlFilter, TargetFilterQueryFields.class)); + } + return convertPage(findTargetFilterQueryByCriteriaAPI(pageable, specList), pageable); + } + + @Override + public Page findTargetFilterQueryWithAutoAssignDS(@NotNull Pageable pageable) { + final List> specList = new ArrayList<>(); + specList.add(TargetFilterQuerySpecification.withAutoAssignDS()); + return convertPage(findTargetFilterQueryByCriteriaAPI(pageable, specList), pageable); + } + private Page findTargetFilterQueryByCriteriaAPI(final Pageable pageable, final List> specList) { if (specList == null || specList.isEmpty()) { 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 225c0b4b2..530d48d58 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 @@ -26,6 +26,7 @@ import javax.persistence.criteria.JoinType; import javax.persistence.criteria.Order; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; +import javax.validation.constraints.NotNull; import org.eclipse.hawkbit.repository.TargetFields; import org.eclipse.hawkbit.repository.TargetManagement; @@ -561,6 +562,31 @@ public class JpaTargetManagement implements TargetManagement { .collect(Collectors.toList()); } + @Override + public Page findAllTargetIdsByTargetFilterQueryAndNonDS(@NotNull Pageable pageRequest, + Long distributionSetId, @NotNull TargetFilterQuery targetFilterQuery) { + + final Specification spec = RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class); + + return findTargetsBySpec( + (root, cq, + cb) -> cb.and(spec.toPredicate(root, cq, cb), TargetSpecifications + .hasNotDistributionSetInActions(distributionSetId).toPredicate(root, cq, cb)), + pageRequest); + + } + + @Override + public Long countTargetByTargetFilterQueryAndNonDS(Long distributionSetId, @NotNull TargetFilterQuery targetFilterQuery) { + final Specification spec = RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class); + final List> specList = new ArrayList<>(); + specList.add(spec); + + specList.add(TargetSpecifications.hasNotDistributionSetInActions(distributionSetId)); + + return countByCriteriaAPI(specList); + } + @PreDestroy void destroy() { eventBus.unregister(this); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/TargetFilterQueryRepository.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/TargetFilterQueryRepository.java index 1f2fec8d8..5ebee7619 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/TargetFilterQueryRepository.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/TargetFilterQueryRepository.java @@ -13,6 +13,8 @@ import org.eclipse.hawkbit.repository.model.TargetFilterQuery; import org.springframework.data.domain.Page; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Transactional; @@ -43,4 +45,15 @@ public interface TargetFilterQueryRepository @Transactional S save(S entity); + /** + * Sets the auto assign distribution sets to null which match the ds ids. + * + * @param dsIds + * distribution set ids to be set to null + */ + @Modifying + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + @Query("update JpaTargetFilterQuery d set d.autoAssignDistributionSet = NULL where d.autoAssignDistributionSet in :ids") + void unsetAutoAssignDistributionSet(@Param("ids") Long... dsIds); + } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignChecker.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignChecker.java new file mode 100644 index 000000000..f35aab453 --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignChecker.java @@ -0,0 +1,168 @@ +/** + * Copyright (c) 2015 Bosch Software Innovations 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 + */ +package org.eclipse.hawkbit.repository.jpa.autoassign; + +import java.util.List; +import java.util.stream.Collectors; + +import org.eclipse.hawkbit.exception.AbstractServerRtException; +import org.eclipse.hawkbit.repository.DeploymentManagement; +import org.eclipse.hawkbit.repository.TargetFilterQueryManagement; +import org.eclipse.hawkbit.repository.TargetManagement; +import org.eclipse.hawkbit.repository.model.Action; +import org.eclipse.hawkbit.repository.model.DistributionSet; +import org.eclipse.hawkbit.repository.model.RepositoryModelConstants; +import org.eclipse.hawkbit.repository.model.Target; +import org.eclipse.hawkbit.repository.model.TargetFilterQuery; +import org.eclipse.hawkbit.repository.model.TargetWithActionType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.stereotype.Component; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.support.DefaultTransactionDefinition; +import org.springframework.transaction.support.TransactionTemplate; + +import javax.persistence.PersistenceException; + +/** + * Checks if targets need a new distribution set (DS) based on the target filter queries and + * assigns the new DS when necessary. + * First all target filter queries are listed. For every target filter query (TFQ) the auto assign DS + * is retrieved. + * All targets get listed per target filter query, that match the TFQ and that don't have the + * auto assign DS in their action history. + */ +@Component +public class AutoAssignChecker { + + private static final Logger LOGGER = LoggerFactory.getLogger(AutoAssignChecker.class); + + @Autowired + private TargetFilterQueryManagement targetFilterQueryManagement; + + @Autowired + private TargetManagement targetManagement; + + @Autowired + private DeploymentManagement deploymentManagement; + + @Autowired + private PlatformTransactionManager transactionManager; + + private TransactionTemplate transactionTemplate; + + + /** + * Maximum for target filter queries with auto assign DS + * Maximum for targets that are fetched in one turn + */ + private static final int PAGE_SIZE = 1000; + + /** + * The message which is added to the action status when a distribution set is assigned + * to an target. First %s is the name of the target filter. + */ + private static final String ACTION_MESSAGE = "Auto assignment by target filter: %s"; + + /** + * Checks all target filter queries with an auto assign distribution set + * and triggers the check and assignment to targets that don't have the design DS yet + */ + public void check() { + if(transactionTemplate == null) { + final DefaultTransactionDefinition def = new DefaultTransactionDefinition(); + def.setName("autoAssignDSToTargets"); + def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); + transactionTemplate = new TransactionTemplate(transactionManager, def); + } + + PageRequest pageRequest = new PageRequest(0, PAGE_SIZE); + + Page filterQueries = targetFilterQueryManagement + .findTargetFilterQueryWithAutoAssignDS(pageRequest); + + for (TargetFilterQuery filterQuery : filterQueries) { + checkByTargetFilterQueryAndAssignDS(filterQuery); + } + + } + + /** + * Fetches the distribution set, gets all controllerIds and assigns the DS + * to them. + * Catches PersistenceException and own exceptions derived from AbstractServerRtException + * + * @param targetFilterQuery + * the target filter query + */ + private void checkByTargetFilterQueryAndAssignDS(TargetFilterQuery targetFilterQuery) { + try { + DistributionSet distributionSet = targetFilterQuery.getAutoAssignDistributionSet(); + + int count; + do { + + count = runTransactionalAssignment(targetFilterQuery, distributionSet.getId()); + + } while (count == PAGE_SIZE); + + } catch (PersistenceException | AbstractServerRtException e) { + LOGGER.error("Error during auto assign check of target filter query " + targetFilterQuery.getId(), e); + } + + } + + /** + * Runs one page of target assignments within a dedicated transaction + * + * @param targetFilterQuery + * the target filter query + * @param dsId + * distribution set id to assign + * @return count of targets + */ + private int runTransactionalAssignment(TargetFilterQuery targetFilterQuery, Long dsId) { + final String actionMessage = String.format(ACTION_MESSAGE, targetFilterQuery.getName()); + return transactionTemplate.execute(status -> { + List targets = getTargetsWithActionType(targetFilterQuery, dsId, PAGE_SIZE); + int count = targets.size(); + if (count > 0) { + deploymentManagement.assignDistributionSet(dsId, targets, actionMessage); + } + return count; + }); + } + + /** + * Gets all matching targets with the designated action from the target management + * + * @param targetFilterQuery + * the query the targets have to match + * @param dsId + * dsId the targets are not allowed to have in their action + * history + * @param count + * maximum amount of targets to retrieve + * @return list of targets with action type + */ + private List getTargetsWithActionType(TargetFilterQuery targetFilterQuery, Long dsId, int count) { + Page targets = targetManagement.findAllTargetIdsByTargetFilterQueryAndNonDS(new PageRequest(0, count), + dsId, targetFilterQuery); + + return targets.getContent().stream() + .map(t -> new TargetWithActionType(t.getControllerId(), Action.ActionType.FORCED, + RepositoryModelConstants.NO_FORCE_TIME)) + .collect(Collectors.toList()); + } + +} diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignScheduler.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignScheduler.java new file mode 100644 index 000000000..81ec130dc --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignScheduler.java @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2015 Bosch Software Innovations 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 + */ +package org.eclipse.hawkbit.repository.jpa.autoassign; + +import java.util.List; + +import org.eclipse.hawkbit.repository.AutoAssignProperties; +import org.eclipse.hawkbit.repository.SystemManagement; +import org.eclipse.hawkbit.security.SystemSecurityContext; +import org.eclipse.hawkbit.tenancy.TenantAware; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Profile; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +/** + * Scheduler to check target filters for auto assignment of distribution sets + */ +@Component +// don't active the auto assign scheduler in test, otherwise it is hard to test +@Profile("!test") +public class AutoAssignScheduler { + + private static final Logger LOGGER = LoggerFactory.getLogger(AutoAssignScheduler.class); + + @Autowired + private TenantAware tenantAware; + + @Autowired + private SystemManagement systemManagement; + + @Autowired + private SystemSecurityContext systemSecurityContext; + + @Autowired + private AutoAssignChecker autoAssignChecker; + + /** + * Scheduler method called by the spring-async mechanism. Retrieves all + * tenants from the {@link SystemManagement#findTenants()} and runs for each + * tenant the auto assignments defined in the target filter queries + * {@link SystemSecurityContext}. + */ + @Scheduled(initialDelayString = AutoAssignProperties.Scheduler.PROP_SCHEDULER_DELAY_PLACEHOLDER, fixedDelayString = AutoAssignProperties.Scheduler.PROP_SCHEDULER_DELAY_PLACEHOLDER) + public void autoAssignScheduler() { + LOGGER.debug("auto assign schedule checker has been triggered."); + // run this code in system code privileged to have the necessary + // permission to query and create entities. + systemSecurityContext.runAsSystem(() -> { + // workaround eclipselink that is currently not possible to + // execute a query without multitenancy if MultiTenant + // annotation is used. + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=355458. So + // iterate through all tenants and execute the rollout check for + // each tenant separately. + final List tenants = systemManagement.findTenants(); + LOGGER.info("Checking target filter queries for tenants: {}", tenants.size()); + for (final String tenant : tenants) { + tenantAware.runAsTenant(tenant, () -> { + + autoAssignChecker.check(); + + return null; + }); + } + return null; + }); + } +} diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSet.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSet.java index 9fc27f1e8..8b5da1f68 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSet.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSet.java @@ -51,6 +51,7 @@ import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.repository.model.Target; +import org.eclipse.hawkbit.repository.model.TargetFilterQuery; import org.eclipse.hawkbit.repository.model.TargetInfo; import org.eclipse.persistence.annotations.CascadeOnDelete; import org.eclipse.persistence.descriptors.DescriptorEvent; @@ -96,6 +97,9 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen @OneToMany(mappedBy = "assignedDistributionSet", targetEntity = JpaTarget.class, fetch = FetchType.LAZY) private List assignedToTargets; + @OneToMany(mappedBy = "autoAssignDistributionSet", targetEntity = JpaTargetFilterQuery.class, fetch = FetchType.LAZY) + private List autoAssignFilters; + @OneToMany(mappedBy = "installedDistributionSet", targetEntity = JpaTargetInfo.class, fetch = FetchType.LAZY) private List installedAtTargets; @@ -196,6 +200,11 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen return assignedToTargets; } + @Override + public List getAutoAssignFilters() { + return autoAssignFilters; + } + @Override public List getInstalledTargets() { return installedAtTargets; diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTargetFilterQuery.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTargetFilterQuery.java index 2a40619b2..b7e80cbb3 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTargetFilterQuery.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTargetFilterQuery.java @@ -9,13 +9,19 @@ package org.eclipse.hawkbit.repository.jpa.model; import javax.persistence.Column; +import javax.persistence.ConstraintMode; import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.ForeignKey; import javax.persistence.Index; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; import javax.persistence.Table; import javax.persistence.UniqueConstraint; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; +import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.TargetFilterQuery; /** @@ -42,6 +48,10 @@ public class JpaTargetFilterQuery extends AbstractJpaTenantAwareBaseEntity imple @NotNull private String query; + @ManyToOne(optional = true, fetch = FetchType.LAZY, targetEntity = JpaDistributionSet.class) + @JoinColumn(name = "auto_assign_distribution_set", nullable = true, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_filter_auto_assign_ds")) + private JpaDistributionSet autoAssignDistributionSet; + public JpaTargetFilterQuery() { // Default constructor for JPA. } @@ -59,6 +69,22 @@ public class JpaTargetFilterQuery extends AbstractJpaTenantAwareBaseEntity imple this.query = query; } + /** + * Construct a Target filter query with auto assign distribution set + * + * @param name + * of the {@link TargetFilterQuery}. + * @param query + * of the {@link TargetFilterQuery}. + * @param autoAssignDistributionSet + * of the {@link TargetFilterQuery}. + */ + public JpaTargetFilterQuery(String name, String query, JpaDistributionSet autoAssignDistributionSet) { + this.name = name; + this.query = query; + this.autoAssignDistributionSet = autoAssignDistributionSet; + } + @Override public String getName() { return name; @@ -78,4 +104,14 @@ public class JpaTargetFilterQuery extends AbstractJpaTenantAwareBaseEntity imple public void setQuery(final String query) { this.query = query; } + + @Override + public DistributionSet getAutoAssignDistributionSet() { + return autoAssignDistributionSet; + } + + @Override + public void setAutoAssignDistributionSet(DistributionSet distributionSet) { + autoAssignDistributionSet = (JpaDistributionSet)distributionSet; + } } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/TargetFilterQuerySpecification.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/TargetFilterQuerySpecification.java index 4b27c7fc7..d783738ae 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/TargetFilterQuerySpecification.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/TargetFilterQuerySpecification.java @@ -10,6 +10,7 @@ package org.eclipse.hawkbit.repository.jpa.specifications; import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery; import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery_; +import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.TargetFilterQuery; import org.springframework.data.jpa.domain.Specification; @@ -37,4 +38,28 @@ public final class TargetFilterQuerySpecification { return cb.like(cb.lower(targetFilterQueryRoot.get(JpaTargetFilterQuery_.name)), searchTextToLower); }; } + + /** + * {@link Specification} for retrieving {@link JpaTargetFilterQuery}s based + * on is {@link JpaTargetFilterQuery#getName()}. + * + * @param distributionSet + * of the filter + * @return the {@link JpaTargetFilterQuery} {@link Specification} + */ + public static Specification byAutoAssignDS(final DistributionSet distributionSet) { + return (targetFilterQueryRoot, query, cb) -> cb + .equal(targetFilterQueryRoot.get(JpaTargetFilterQuery_.autoAssignDistributionSet), distributionSet); + } + + /** + * {@link Specification} for retrieving {@link JpaTargetFilterQuery}s based + * on is {@link JpaTargetFilterQuery#getName()}. + * + * @return the {@link JpaTargetFilterQuery} {@link Specification} + */ + public static Specification withAutoAssignDS() { + return (targetFilterQueryRoot, query, cb) -> cb + .isNotNull(targetFilterQueryRoot.get(JpaTargetFilterQuery_.autoAssignDistributionSet)); + } } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/TargetSpecifications.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/TargetSpecifications.java index 6571f8668..dbf0e4e23 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/TargetSpecifications.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/TargetSpecifications.java @@ -14,12 +14,15 @@ import java.util.List; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.Join; import javax.persistence.criteria.JoinType; +import javax.persistence.criteria.ListJoin; import javax.persistence.criteria.Path; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import javax.persistence.criteria.SetJoin; import javax.validation.constraints.NotNull; +import org.eclipse.hawkbit.repository.jpa.model.JpaAction; +import org.eclipse.hawkbit.repository.jpa.model.JpaAction_; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_; import org.eclipse.hawkbit.repository.jpa.model.JpaTarget; @@ -210,6 +213,24 @@ public final class TargetSpecifications { distributionSetId); } + /** + * {@link Specification} for retrieving {@link Target}s that don't have the given + * distribution set in their action history + * + * @param distributionSetId + * the ID of the distribution set which must not be assigned + * @return the {@link Target} {@link Specification} + */ + public static Specification hasNotDistributionSetInActions(final Long distributionSetId) { + return (targetRoot, query, cb) -> { + final ListJoin actionsJoin = targetRoot.join(JpaTarget_.actions, JoinType.LEFT); + actionsJoin.on(cb.equal(actionsJoin.get(JpaAction_.distributionSet).get(JpaDistributionSet_.id), + distributionSetId)); + + return cb.isNull(actionsJoin.get(JpaAction_.id)); + }; + } + /** * {@link Specification} for retrieving {@link Target}s by assigned * distribution set. diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/H2/V1_8_0__auto_assign_ds_filter__H2.sql b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/H2/V1_8_0__auto_assign_ds_filter__H2.sql new file mode 100644 index 000000000..2d56f27f2 --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/H2/V1_8_0__auto_assign_ds_filter__H2.sql @@ -0,0 +1,8 @@ +ALTER TABLE sp_target_filter_query + ADD column auto_assign_distribution_set BIGINT; + +ALTER TABLE sp_target_filter_query + ADD CONSTRAINT fk_filter_auto_assign_ds +FOREIGN KEY (auto_assign_distribution_set) +REFERENCES sp_distribution_set +ON DELETE SET NULL; diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/MYSQL/V1_8_0__auto_assign_ds_filter__MYSQL.sql b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/MYSQL/V1_8_0__auto_assign_ds_filter__MYSQL.sql new file mode 100644 index 000000000..e56a6d242 --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/MYSQL/V1_8_0__auto_assign_ds_filter__MYSQL.sql @@ -0,0 +1,8 @@ +ALTER TABLE sp_target_filter_query + ADD COLUMN auto_assign_distribution_set BIGINT; + +ALTER TABLE sp_target_filter_query + ADD CONSTRAINT fk_filter_auto_assign_ds +FOREIGN KEY (auto_assign_distribution_set) +REFERENCES sp_distribution_set (id) +ON DELETE SET NULL; diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetFilterQueryManagenmentTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetFilterQueryManagenmentTest.java index 07b92ebeb..b51511d17 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetFilterQueryManagenmentTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetFilterQueryManagenmentTest.java @@ -8,19 +8,27 @@ */ package org.eclipse.hawkbit.repository.jpa; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - import org.eclipse.hawkbit.repository.TargetFilterQueryManagement; import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException; +import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet; import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery; +import org.eclipse.hawkbit.repository.model.DistributionSet; +import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.TargetFilterQuery; import org.junit.Test; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; import ru.yandex.qatools.allure.annotations.Description; import ru.yandex.qatools.allure.annotations.Features; import ru.yandex.qatools.allure.annotations.Stories; +import java.util.Iterator; +import java.util.List; + +import static org.fest.assertions.api.Assertions.assertThat; +import static org.junit.Assert.*; + /** * Test class for {@link TargetFilterQueryManagement}. * @@ -39,6 +47,22 @@ public class TargetFilterQueryManagenmentTest extends AbstractJpaIntegrationTest targetFilterQueryManagement.findTargetFilterQueryByName(filterName)); } + @Test + @Description("Test searching a target filter query.") + public void searchTargetFilterQuery() { + final String filterName = "targetFilterQueryName"; + final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement + .createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001")); + + targetFilterQueryManagement + .createTargetFilterQuery(new JpaTargetFilterQuery("someOtherFilter", "name==PendingTargets002")); + + List results = targetFilterQueryManagement + .findTargetFilterQueryByFilter(new PageRequest(0, 10), "name==" + filterName).getContent(); + assertEquals("Search result should have 1 result", 1, results.size()); + assertEquals("Retrieved newly created custom target filter", targetFilterQuery, results.get(0)); + } + @Test @Description("Checks if the EntityAlreadyExistsException is thrown if a targetfilterquery with the same name are created more than once.") public void createDuplicateTargetFilterQuery() { @@ -82,4 +106,142 @@ public class TargetFilterQueryManagenmentTest extends AbstractJpaIntegrationTest } + @Test + @Description("Test assigning a distribution set") + public void assignDistributionSet() { + final String filterName = "target_filter_02"; + final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement + .createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001")); + + final DistributionSet distributionSet = distributionSetManagement.createDistributionSet(new JpaDistributionSet( + "dist_Set_01", "0.1", "", null, null + )); + + targetFilterQuery.setAutoAssignDistributionSet(distributionSet); + targetFilterQueryManagement.updateTargetFilterQuery(targetFilterQuery); + + TargetFilterQuery tfq = targetFilterQueryManagement.findTargetFilterQueryByName(filterName); + + assertEquals("Returns correct distribution set", distributionSet, + tfq.getAutoAssignDistributionSet()); + + } + + @Test + @Description("Test removing distribution set while it has a relation to a target filter query") + public void removeAssignDistributionSet() { + final String filterName = "target_filter_03"; + final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement + .createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001")); + + final DistributionSet distributionSet = distributionSetManagement.createDistributionSet(new JpaDistributionSet( + "dist_Set_02", "0.1", "", null, null + )); + + targetFilterQuery.setAutoAssignDistributionSet(distributionSet); + targetFilterQueryManagement.updateTargetFilterQuery(targetFilterQuery); + + // Check if target filter query is there + TargetFilterQuery tfq = targetFilterQueryManagement.findTargetFilterQueryByName(filterName); + assertEquals("Returns correct distribution set", distributionSet, + tfq.getAutoAssignDistributionSet()); + + distributionSetManagement.deleteDistributionSet(distributionSet); + + // Check if auto assign distribution set is null + tfq = targetFilterQueryManagement.findTargetFilterQueryByName(filterName); + assertNotNull("Returns target filter query", tfq); + assertNull("Returns distribution set as null", tfq.getAutoAssignDistributionSet()); + + } + + @Test + @Description("Test to implicitly remove the auto assign distribution set when the ds is soft deleted") + public void implicitlyRemoveAssignDistributionSet() { + final String filterName = "target_filter_03"; + DistributionSet distributionSet = testdataFactory.createDistributionSet("dist_set"); + Target target = testdataFactory.createTarget(); + + // Assign the distribution set to an target, to force a soft delete in a + // later step + deploymentManagement.assignDistributionSet(distributionSet.getId(), target.getControllerId()); + + targetFilterQueryManagement.createTargetFilterQuery( + new JpaTargetFilterQuery(filterName, "name==PendingTargets001", (JpaDistributionSet) distributionSet)); + + // Check if target filter query is there with the distribution set + TargetFilterQuery tfq = targetFilterQueryManagement.findTargetFilterQueryByName(filterName); + assertEquals("Returns correct distribution set", distributionSet, tfq.getAutoAssignDistributionSet()); + + distributionSetManagement.deleteDistributionSet(distributionSet); + + // Check if distribution set is still in the database with deleted flag + assertTrue("Distribution set should be deleted", + distributionSetManagement.findDistributionSetById(distributionSet.getId()).isDeleted()); + + // Check if auto assign distribution set is null + tfq = targetFilterQueryManagement.findTargetFilterQueryByName(filterName); + assertNotNull("Returns target filter query", tfq); + assertNull("Returns distribution set as null", tfq.getAutoAssignDistributionSet()); + + } + + @Test + @Description("Test finding and auto assign distribution set") + public void findFiltersWithDistributionSet() { + + final String filterName = "d"; + + assertEquals(0L, targetFilterQueryManagement.countAllTargetFilterQuery().longValue()); + + targetFilterQueryManagement.createTargetFilterQuery(new JpaTargetFilterQuery("a", "name==*")); + targetFilterQueryManagement.createTargetFilterQuery(new JpaTargetFilterQuery("b", "name==*")); + + final DistributionSet distributionSet = distributionSetManagement + .createDistributionSet(new JpaDistributionSet("dist_Set_01", "0.1", "", null, null)); + final DistributionSet distributionSet2 = distributionSetManagement + .createDistributionSet(new JpaDistributionSet("dist_Set_02", "0.1", "", null, null)); + + final TargetFilterQuery tfq = targetFilterQueryManagement.createTargetFilterQuery( + new JpaTargetFilterQuery("c", "name==x", (JpaDistributionSet) distributionSet)); + + final TargetFilterQuery tfq2 = targetFilterQueryManagement.createTargetFilterQuery( + new JpaTargetFilterQuery(filterName, "name==z*", (JpaDistributionSet) distributionSet2)); + + assertEquals(4L, targetFilterQueryManagement.countAllTargetFilterQuery().longValue()); + + // check if find works + Page tfqList = targetFilterQueryManagement + .findTargetFilterQueryByAutoAssignDS(new PageRequest(0, 500), distributionSet); + assertThat(1L).as("Target filter query").isEqualTo(tfqList.getTotalElements()); + + assertEquals("Returns correct target filter query", tfq.getId(), tfqList.iterator().next().getId()); + + tfq2.setAutoAssignDistributionSet(distributionSet); + targetFilterQueryManagement.updateTargetFilterQuery(tfq2); + + // check if find works for two + tfqList = targetFilterQueryManagement.findTargetFilterQueryByAutoAssignDS(new PageRequest(0, 500), + distributionSet); + assertThat(2L).as("Target filter query count").isEqualTo(tfqList.getTotalElements()); + Iterator iterator = tfqList.iterator(); + assertEquals("Returns correct target filter query 1", tfq.getId(), iterator.next().getId()); + assertEquals("Returns correct target filter query 2", tfq2.getId(), iterator.next().getId()); + + // check if find works with name filter + tfqList = targetFilterQueryManagement.findTargetFilterQueryByAutoAssignDS(new PageRequest(0, 500), + distributionSet, "name==" + filterName); + assertThat(1L).as("Target filter query count").isEqualTo(tfqList.getTotalElements()); + + assertEquals("Returns correct target filter query", tfq2.getId(), tfqList.iterator().next().getId()); + + // check if find works for all with auto assign DS + tfqList = targetFilterQueryManagement.findTargetFilterQueryWithAutoAssignDS(new PageRequest(0, 500)); + assertThat(2L).as("Target filter query count").isEqualTo(tfqList.getTotalElements()); + iterator = tfqList.iterator(); + assertEquals("Returns correct target filter query 1", tfq.getId(), iterator.next().getId()); + assertEquals("Returns correct target filter query 2", tfq2.getId(), iterator.next().getId()); + + } + } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementSearchTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementSearchTest.java index 1e8f102c7..58856f16c 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementSearchTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementSearchTest.java @@ -22,16 +22,10 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus; import org.eclipse.hawkbit.repository.jpa.model.JpaTarget; import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery; import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag; -import org.eclipse.hawkbit.repository.model.Action; +import org.eclipse.hawkbit.repository.model.*; import org.eclipse.hawkbit.repository.model.Action.Status; -import org.eclipse.hawkbit.repository.model.ActionStatus; -import org.eclipse.hawkbit.repository.model.DistributionSet; -import org.eclipse.hawkbit.repository.model.Target; -import org.eclipse.hawkbit.repository.model.TargetIdName; -import org.eclipse.hawkbit.repository.model.TargetTag; -import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; -import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity; import org.junit.Test; +import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Slice; import com.google.common.collect.Lists; @@ -727,6 +721,26 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest { } + @Test + @Description("Verifies that targets without given assigned DS are returned from repository.") + public void findTargetWithoutAssignedDistributionSet() { + final DistributionSet assignedSet = testdataFactory.createDistributionSet(""); + final TargetFilterQuery tfq = targetFilterQueryManagement + .createTargetFilterQuery(entityFactory.generateTargetFilterQuery("tfq", "name==*")); + List unassignedTargets = targetManagement + .createTargets(testdataFactory.generateTargets(12, "unassigned")); + List assignedTargets = targetManagement.createTargets(testdataFactory.generateTargets(10, "assigned")); + + deploymentManagement.assignDistributionSet(assignedSet, assignedTargets); + + List result = targetManagement.findAllTargetIdsByTargetFilterQueryAndNonDS(pageReq, + assignedSet.getId(), tfq).getContent(); + assertThat(result) + .as("count of targets").hasSize(unassignedTargets.size()) + .as("contains all targets").containsAll(unassignedTargets); + + } + @Test @Description("Verfies that targets with given installed DS are returned from repository.") public void findTargetByInstalledDistributionSet() { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignCheckerTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignCheckerTest.java new file mode 100644 index 000000000..dd7293fb2 --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignCheckerTest.java @@ -0,0 +1,157 @@ +/** + * Copyright (c) 2015 Bosch Software Innovations 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 + */ +package org.eclipse.hawkbit.repository.jpa.autoassign; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest; +import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet; +import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery; +import org.eclipse.hawkbit.repository.model.DistributionSet; +import org.eclipse.hawkbit.repository.model.DistributionSetType; +import org.eclipse.hawkbit.repository.model.Target; +import org.eclipse.hawkbit.repository.model.TargetFilterQuery; +import org.junit.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Slice; +import ru.yandex.qatools.allure.annotations.Description; +import ru.yandex.qatools.allure.annotations.Features; +import ru.yandex.qatools.allure.annotations.Step; +import ru.yandex.qatools.allure.annotations.Stories; + +import static org.fest.assertions.api.Assertions.assertThat; + +/** + * Test class for {@link AutoAssignChecker}. + * + */ +@Features("Component Tests - Repository") +@Stories("Auto assign checker") +public class AutoAssignCheckerTest extends AbstractJpaIntegrationTest { + + @Autowired + private AutoAssignChecker autoAssignChecker; + + @Test + @Description("Test auto assignment of a DS to filtered targets") + public void checkAutoAssign() { + + final DistributionSet setA = testdataFactory.createDistributionSet("dsA"); // will be auto assigned + final DistributionSet setB = testdataFactory.createDistributionSet("dsB"); + + // target filter query that matches all targets + TargetFilterQuery targetFilterQuery = targetFilterQueryManagement + .createTargetFilterQuery(new JpaTargetFilterQuery("filterA", "name==*")); + targetFilterQuery.setAutoAssignDistributionSet(setA); + targetFilterQueryManagement.updateTargetFilterQuery(targetFilterQuery); + + + final String targetDsAIdPref = "targ"; + List targets = targetManagement.createTargets( + testdataFactory.generateTargets(100, targetDsAIdPref, targetDsAIdPref.concat(" description"))); + int targetsCount = targets.size(); + + // assign set A to first 10 targets + deploymentManagement.assignDistributionSet(setA, targets.subList(0, 10)); + verifyThatTargetsHaveDistributionSetAssignment(setA, targets.subList(0, 10), targetsCount); + + // assign set B to first 5 targets + // they have now 2 DS in their action history and should not get updated with dsA + deploymentManagement.assignDistributionSet(setB, targets.subList(0, 5)); + verifyThatTargetsHaveDistributionSetAssignment(setB, targets.subList(0, 5), targetsCount); + + // assign set B to next 10 targets + deploymentManagement.assignDistributionSet(setB, targets.subList(10, 20)); + verifyThatTargetsHaveDistributionSetAssignment(setB, targets.subList(10, 20), targetsCount); + + // Count the number of targets that will be assigned with setA + assertThat(targetManagement.countTargetByTargetFilterQueryAndNonDS(setA.getId(), targetFilterQuery)) + .isEqualTo(90); + + // Run the check + autoAssignChecker.check(); + + verifyThatTargetsHaveDistributionSetAssignment(setA, targets.subList(5, 100), targetsCount); + + // first 5 should keep their dsB, because they already had the dsA once + verifyThatTargetsHaveDistributionSetAssignment(setB, targets.subList(0, 5), targetsCount); + + } + + @Test + @Description("Test auto assignment of an incomplete DS to filtered targets, that causes failures") + public void checkAutoAssignWithFailures() { + + // incomplete distribution set that will be assigned + final DistributionSet setF = distributionSetManagement + .createDistributionSet(entityFactory.generateDistributionSet("dsA", "1", "incomplete ds", + testdataFactory.findOrCreateDefaultTestDsType(), null)); + final DistributionSet setA = testdataFactory.createDistributionSet("dsA"); + final DistributionSet setB = testdataFactory.createDistributionSet("dsB"); + + final String targetDsAIdPref = "targA"; + final String targetDsFIdPref = "targB"; + + // target filter query that matches first bunch of targets, that should + // fail + targetFilterQueryManagement.createTargetFilterQuery( + new JpaTargetFilterQuery("filterA", "id==" + targetDsFIdPref + "*", (JpaDistributionSet) setF)); + + // target filter query that matches failed bunch of targets + targetFilterQueryManagement.createTargetFilterQuery( + new JpaTargetFilterQuery("filterB", "id==" + targetDsAIdPref + "*", (JpaDistributionSet) setA)); + + List targetsF = targetManagement.createTargets( + testdataFactory.generateTargets(10, targetDsFIdPref, targetDsFIdPref.concat(" description"))); + + List targetsA = targetManagement.createTargets( + testdataFactory.generateTargets(10, targetDsAIdPref, targetDsAIdPref.concat(" description"))); + + int targetsCount = targetsA.size() + targetsF.size(); + + // assign set B to first 5 targets of fail group + deploymentManagement.assignDistributionSet(setB, targetsF.subList(0, 5)); + verifyThatTargetsHaveDistributionSetAssignment(setB, targetsF.subList(0, 5), targetsCount); + + // Run the check + autoAssignChecker.check(); + + // first 5 targets of the fail group should still have setB + verifyThatTargetsHaveDistributionSetAssignment(setB, targetsF.subList(0, 5), targetsCount); + + // all targets of A group should have received setA + verifyThatTargetsHaveDistributionSetAssignment(setA, targetsA, targetsCount); + + } + + /** + * @param set the expected distribution set + * @param targets the targets that should have it + */ + @Step + private void verifyThatTargetsHaveDistributionSetAssignment(final DistributionSet set, List targets, int count) { + List targetIds = targets.stream().map(Target::getId).collect(Collectors.toList()); + + Slice targetsAll = targetManagement.findTargetsAll(new PageRequest(0, 1000)); + assertThat(targetsAll).as("Count of targets").hasSize(count); + + for (Target target : targetsAll) { + if(targetIds.contains(target.getId())) { + assertThat(target.getAssignedDistributionSet()).as("assigned DS").isEqualTo(set); + } + } + + } + +} \ No newline at end of file diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/CommonDialogWindow.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/CommonDialogWindow.java index 418944582..f73c1875a 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/CommonDialogWindow.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/CommonDialogWindow.java @@ -263,9 +263,9 @@ public class CommonDialogWindow extends Window { if (field instanceof Table) { ((Table) field).addItemSetChangeListener(new ChangeListener(field)); - } else { - field.addValueChangeListener(new ChangeListener(field)); } + field.addValueChangeListener(new ChangeListener(field)); + } } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/detailslayout/TargetFilterQueryDetailsTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/detailslayout/TargetFilterQueryDetailsTable.java new file mode 100644 index 000000000..3371e0d64 --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/detailslayout/TargetFilterQueryDetailsTable.java @@ -0,0 +1,107 @@ +/** + * Copyright (c) 2015 Bosch Software Innovations 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 + */ +package org.eclipse.hawkbit.ui.common.detailslayout; + +import java.util.List; + +import com.vaadin.data.Container; +import com.vaadin.data.Item; +import org.eclipse.hawkbit.repository.model.DistributionSet; +import org.eclipse.hawkbit.repository.model.TargetFilterQuery; + +import com.vaadin.data.util.IndexedContainer; +import com.vaadin.spring.annotation.SpringComponent; +import com.vaadin.spring.annotation.VaadinSessionScope; +import com.vaadin.ui.Table; +import com.vaadin.ui.themes.ValoTheme; +import org.eclipse.hawkbit.ui.utils.I18N; +import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions; + +/** + * + * DistributionSet TargetFilterQuery table + * + */ + +@SpringComponent +@VaadinSessionScope +public class TargetFilterQueryDetailsTable extends Table { + + private static final long serialVersionUID = 2913758299611837718L; + + private static final String TFQ_NAME = "name"; + private static final String TFQ_QUERY = "query"; + + private I18N i18n; + + /** + * + * @param i18n + */ + public void init(final I18N i18n) { + this.i18n = i18n; + createTable(); + } + + /** + * Populate software module metadata. + * + * @param distributionSet the selected distribution set + */ + public void populateTableByDistributionSet(final DistributionSet distributionSet) { + removeAllItems(); + if (null == distributionSet) { + return; + } + + Container dataSource = getContainerDataSource(); + List filters = distributionSet.getAutoAssignFilters(); + filters.forEach(query -> { + Object itemId = dataSource.addItem(); + Item item = dataSource.getItem(itemId); + item.getItemProperty(TFQ_NAME).setValue(query.getName()); + item.getItemProperty(TFQ_QUERY).setValue(query.getQuery()); + }); + + } + + private void createTable() { + addStyleName(ValoTheme.TABLE_NO_HORIZONTAL_LINES); + addStyleName(ValoTheme.TABLE_NO_STRIPES); + addStyleName(SPUIStyleDefinitions.SW_MODULE_TABLE); + addStyleName("details-layout"); + setSelectable(false); + setImmediate(true); + setContainerDataSource(getDistSetContainer()); + setColumnHeaderMode(ColumnHeaderMode.EXPLICIT); + addTableHeader(); + setSizeFull(); + // same as height of other tabs in details tabsheet + setHeight(116, Unit.PIXELS); + } + + private IndexedContainer getDistSetContainer() { + final IndexedContainer container = new IndexedContainer(); + container.addContainerProperty(TFQ_NAME, String.class, ""); + container.addContainerProperty(TFQ_QUERY, String.class, ""); + setColumnExpandRatio(TFQ_NAME, 0.4F); + setColumnAlignment(TFQ_NAME, Align.LEFT); + setColumnExpandRatio(TFQ_QUERY, 0.6F); + setColumnAlignment(TFQ_QUERY, Align.LEFT); + + return container; + } + + private void addTableHeader() { + setColumnHeader(TFQ_NAME, i18n.get("header.target.filter.name")); + setColumnHeader(TFQ_QUERY, i18n.get("header.target.filter.query")); + } + + +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/components/ProxyDistribution.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/components/ProxyDistribution.java index 0420f9e4b..fccf7d6a7 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/components/ProxyDistribution.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/components/ProxyDistribution.java @@ -10,6 +10,9 @@ package org.eclipse.hawkbit.ui.components; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.ui.common.DistributionSetIdName; +import org.eclipse.hawkbit.ui.common.UserDetailsFormatter; +import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil; +import org.eclipse.hawkbit.ui.utils.SPDateTimeUtil; /** * Proxy for {@link DistributionSet}. @@ -39,6 +42,33 @@ public class ProxyDistribution { private String version; private String description; + /** + * Creates an empty proxy distribution set + */ + public ProxyDistribution() { + // Default constructor + } + + /** + * Creates a new proxy distribution set by using the values from a distribution set + * @param distributionSet the source distribution set + */ + public ProxyDistribution(DistributionSet distributionSet) { + setName(distributionSet.getName()); + setDescription(distributionSet.getDescription()); + setDistId(distributionSet.getId()); + setId(distributionSet.getId()); + setVersion(distributionSet.getVersion()); + setCreatedDate(SPDateTimeUtil.getFormattedDate(distributionSet.getCreatedAt())); + setLastModifiedDate(SPDateTimeUtil.getFormattedDate(distributionSet.getLastModifiedAt())); + setCreatedByUser(UserDetailsFormatter.loadAndFormatCreatedBy(distributionSet)); + setModifiedByUser(UserDetailsFormatter.loadAndFormatLastModifiedBy(distributionSet)); + setNameVersion( + HawkbitCommonUtil.getFormattedNameVersion(distributionSet.getName(), distributionSet.getVersion())); + setIsComplete(distributionSet.isComplete()); + } + + /** * @return the nameVersion */ diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/components/ProxyTargetFilter.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/components/ProxyTargetFilter.java index 9faf501e0..75d8d343c 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/components/ProxyTargetFilter.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/components/ProxyTargetFilter.java @@ -26,6 +26,7 @@ public class ProxyTargetFilter { private String createdBy; private String lastModifiedBy; private String query; + private ProxyDistribution autoAssignDistributionSet; public String getCreatedDate() { return createdDate; @@ -90,4 +91,11 @@ public class ProxyTargetFilter { this.createdBy = createdBy; } + public ProxyDistribution getAutoAssignDistributionSet() { + return autoAssignDistributionSet; + } + + public void setAutoAssignDistributionSet(ProxyDistribution autoAssignDistributionSet) { + this.autoAssignDistributionSet = autoAssignDistributionSet; + } } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DistributionSetDetails.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DistributionSetDetails.java index a2ded014d..969b2e63a 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DistributionSetDetails.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DistributionSetDetails.java @@ -25,6 +25,7 @@ import org.eclipse.hawkbit.ui.common.DistributionSetIdName; import org.eclipse.hawkbit.ui.common.detailslayout.AbstractNamedVersionedEntityTableDetailsLayout; import org.eclipse.hawkbit.ui.common.detailslayout.DistributionSetMetadatadetailslayout; import org.eclipse.hawkbit.ui.common.detailslayout.SoftwareModuleDetailsTable; +import org.eclipse.hawkbit.ui.common.detailslayout.TargetFilterQueryDetailsTable; import org.eclipse.hawkbit.ui.common.tagdetails.DistributionTagToken; import org.eclipse.hawkbit.ui.components.SPUIComponentProvider; import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleSmallNoBorder; @@ -91,6 +92,8 @@ public class DistributionSetDetails extends AbstractNamedVersionedEntityTableDet private DistributionSetMetadatadetailslayout dsMetadataTable; + private TargetFilterQueryDetailsTable tfqDetailsTable; + private VerticalLayout tagsLayout; private final Map assignedSWModule = new HashMap<>(); @@ -103,9 +106,14 @@ public class DistributionSetDetails extends AbstractNamedVersionedEntityTableDet softwareModuleTable = new SoftwareModuleDetailsTable(); softwareModuleTable.init(getI18n(), true, getPermissionChecker(), distributionSetManagement, getEventBus(), manageDistUIState); + dsMetadataTable = new DistributionSetMetadatadetailslayout(); dsMetadataTable.init(getI18n(), getPermissionChecker(), distributionSetManagement, dsMetadataPopupLayout, entityFactory); + + tfqDetailsTable = new TargetFilterQueryDetailsTable(); + tfqDetailsTable.init(getI18n()); + super.init(); } @@ -120,6 +128,7 @@ public class DistributionSetDetails extends AbstractNamedVersionedEntityTableDet populateModule(); populateTags(); populateMetadataDetails(); + populateTargetFilterQueries(); } private void populateModule() { @@ -268,6 +277,10 @@ public class DistributionSetDetails extends AbstractNamedVersionedEntityTableDet dsMetadataTable.populateDSMetadata(getSelectedBaseEntity()); } + protected void populateTargetFilterQueries() { + tfqDetailsTable.populateTableByDistributionSet(getSelectedBaseEntity()); + } + private void updateDistributionSetDetailsLayout(final String type, final Boolean isMigrationRequired) { final VerticalLayout detailsTabLayout = getDetailsLayout(); detailsTabLayout.removeAllComponents(); @@ -323,6 +336,7 @@ public class DistributionSetDetails extends AbstractNamedVersionedEntityTableDet detailsTab.addTab(createTagsLayout(), getI18n().get("caption.tags.tab"), null); detailsTab.addTab(createLogLayout(), getI18n().get("caption.logs.tab"), null); detailsTab.addTab(dsMetadataTable, getI18n().get("caption.metadata"), null); + detailsTab.addTab(tfqDetailsTable, getI18n().get("caption.auto.assignment.ds"), null); } @Override diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/ManageDistBeanQuery.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/ManageDistBeanQuery.java index a3a08f2b7..c6443d042 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/ManageDistBeanQuery.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/ManageDistBeanQuery.java @@ -20,10 +20,8 @@ import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSetFilter; import org.eclipse.hawkbit.repository.model.DistributionSetFilter.DistributionSetFilterBuilder; import org.eclipse.hawkbit.repository.model.DistributionSetType; -import org.eclipse.hawkbit.ui.common.UserDetailsFormatter; import org.eclipse.hawkbit.ui.components.ProxyDistribution; import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil; -import org.eclipse.hawkbit.ui.utils.SPDateTimeUtil; import org.eclipse.hawkbit.ui.utils.SPUIDefinitions; import org.eclipse.hawkbit.ui.utils.SpringContextHelper; import org.springframework.data.domain.Page; @@ -48,6 +46,7 @@ public class ManageDistBeanQuery extends AbstractBeanQuery { private transient Page firstPageDistributionSets = null; private DistributionSetType distributionSetType = null; + private Boolean dsComplete = null; /** * @@ -69,6 +68,9 @@ public class ManageDistBeanQuery extends AbstractBeanQuery { distributionSetType = (DistributionSetType) queryConfig .get(SPUIDefinitions.FILTER_BY_DISTRIBUTION_SET_TYPE); } + if(null != queryConfig.get(SPUIDefinitions.FILTER_BY_DS_COMPLETE)) { + dsComplete = (Boolean)queryConfig.get(SPUIDefinitions.FILTER_BY_DS_COMPLETE); + } } if (sortStates.length > 0) { @@ -96,27 +98,17 @@ public class ManageDistBeanQuery extends AbstractBeanQuery { } else if (Strings.isNullOrEmpty(searchText)) { // if no search filters available distBeans = getDistributionSetManagement().findDistributionSetsByDeletedAndOrCompleted( - new OffsetBasedPageRequest(startIndex, count, sort), false, null); + new OffsetBasedPageRequest(startIndex, count, sort), false, dsComplete); } else { final DistributionSetFilter distributionSetFilter = new DistributionSetFilterBuilder().setIsDeleted(false) + .setIsComplete(dsComplete) .setSearchText(searchText).setSelectDSWithNoTag(Boolean.FALSE).setType(distributionSetType).build(); distBeans = getDistributionSetManagement().findDistributionSetsByFilters( new PageRequest(startIndex / count, count, sort), distributionSetFilter); } for (final DistributionSet distributionSet : distBeans) { - final ProxyDistribution proxyDistribution = new ProxyDistribution(); - proxyDistribution.setName(distributionSet.getName()); - proxyDistribution.setDescription(distributionSet.getDescription()); - proxyDistribution.setDistId(distributionSet.getId()); - proxyDistribution.setId(distributionSet.getId()); - proxyDistribution.setVersion(distributionSet.getVersion()); - proxyDistribution.setCreatedDate(SPDateTimeUtil.getFormattedDate(distributionSet.getCreatedAt())); - proxyDistribution.setLastModifiedDate(SPDateTimeUtil.getFormattedDate(distributionSet.getLastModifiedAt())); - proxyDistribution.setCreatedByUser(UserDetailsFormatter.loadAndFormatCreatedBy(distributionSet)); - proxyDistribution.setModifiedByUser(UserDetailsFormatter.loadAndFormatLastModifiedBy(distributionSet)); - proxyDistribution.setIsComplete(distributionSet.isComplete()); - proxyDistributions.add(proxyDistribution); + proxyDistributions.add(new ProxyDistribution(distributionSet)); } return proxyDistributions; } @@ -132,9 +124,10 @@ public class ManageDistBeanQuery extends AbstractBeanQuery { if (Strings.isNullOrEmpty(searchText) && null == distributionSetType) { // if no search filters available firstPageDistributionSets = getDistributionSetManagement().findDistributionSetsByDeletedAndOrCompleted( - new PageRequest(0, SPUIDefinitions.PAGE_SIZE, sort), false, null); + new PageRequest(0, SPUIDefinitions.PAGE_SIZE, sort), false, dsComplete); } else { final DistributionSetFilter distributionSetFilter = new DistributionSetFilterBuilder().setIsDeleted(false) + .setIsComplete(dsComplete) .setSearchText(searchText).setSelectDSWithNoTag(Boolean.FALSE).setType(distributionSetType).build(); firstPageDistributionSets = getDistributionSetManagement().findDistributionSetsByFilters( new PageRequest(0, SPUIDefinitions.PAGE_SIZE, sort), distributionSetFilter); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/DistributionSetSelectTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/DistributionSetSelectTable.java new file mode 100644 index 000000000..516a5449b --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/DistributionSetSelectTable.java @@ -0,0 +1,182 @@ +/** + * Copyright (c) 2015 Bosch Software Innovations 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 + */ +package org.eclipse.hawkbit.ui.filtermanagement; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; + +import org.eclipse.hawkbit.repository.eventbus.event.DistributionCreatedEvent; +import org.eclipse.hawkbit.repository.eventbus.event.DistributionDeletedEvent; +import org.eclipse.hawkbit.ui.distributions.dstable.ManageDistBeanQuery; +import org.eclipse.hawkbit.ui.distributions.state.ManageDistUIState; +import org.eclipse.hawkbit.ui.utils.I18N; +import org.eclipse.hawkbit.ui.utils.SPUIDefinitions; +import org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions; +import org.eclipse.hawkbit.ui.utils.TableColumn; +import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; +import org.springframework.beans.factory.annotation.Autowired; +import org.vaadin.addons.lazyquerycontainer.BeanQueryFactory; +import org.vaadin.addons.lazyquerycontainer.LazyQueryContainer; +import org.vaadin.addons.lazyquerycontainer.LazyQueryDefinition; +import org.vaadin.spring.events.EventBus; +import org.vaadin.spring.events.EventScope; +import org.vaadin.spring.events.annotation.EventBusListenerMethod; + +import com.vaadin.data.Container; +import com.vaadin.spring.annotation.SpringComponent; +import com.vaadin.spring.annotation.ViewScope; +import com.vaadin.ui.Table; +import com.vaadin.ui.themes.ValoTheme; + +/** + * Table for selecting a distribution set. + */ +@SpringComponent +@ViewScope +public class DistributionSetSelectTable extends Table { + + private static final long serialVersionUID = -4307487829435471759L; + + @Autowired + private I18N i18n; + + @Autowired + private transient EventBus.SessionEventBus eventBus; + + @Autowired + private ManageDistUIState manageDistUIState; + + private Container container; + + /** + * Initialize the component. + */ + @PostConstruct + protected void init() { + setStyleName("sp-table"); + setSizeFull(); + setSelectable(true); + setMultiSelect(false); + setImmediate(true); + addStyleName(ValoTheme.TABLE_NO_VERTICAL_LINES); + addStyleName(ValoTheme.TABLE_SMALL); + populateTableData(); + setColumnCollapsingAllowed(false); + setColumnProperties(); + setId(UIComponentIdProvider.DIST_SET_SELECT_TABLE_ID); + eventBus.subscribe(this); + } + + @PreDestroy + void destroy() { + eventBus.unsubscribe(this); + } + + @EventBusListenerMethod(scope = EventScope.SESSION) + void onEvents(final List events) { + final Object firstEvent = events.get(0); + if (DistributionCreatedEvent.class.isInstance(firstEvent) + || DistributionDeletedEvent.class.isInstance(firstEvent)) { + refreshDistributions(); + } + } + + private void populateTableData() { + container = createContainer(); + addContainerproperties(); + setContainerDataSource(container); + setColumnProperties(); + + } + + protected Container createContainer() { + + final Map queryConfiguration = prepareQueryConfigFilters(); + final BeanQueryFactory distributionQF = new BeanQueryFactory<>(ManageDistBeanQuery.class); + + distributionQF.setQueryConfiguration(queryConfiguration); + return new LazyQueryContainer( + new LazyQueryDefinition(true, SPUIDefinitions.PAGE_SIZE, SPUILabelDefinitions.VAR_DIST_ID_NAME), + distributionQF); + } + + private void addContainerproperties() { + /* Create HierarchicalContainer container */ + container.addContainerProperty(SPUILabelDefinitions.NAME, String.class, null); + container.addContainerProperty(SPUILabelDefinitions.VAR_VERSION, String.class, null); + } + + private List getVisbleColumns() { + final List columnList = new ArrayList<>(); + columnList.add(new TableColumn(SPUILabelDefinitions.NAME, i18n.get("header.name"), 0.6F)); + columnList.add(new TableColumn(SPUILabelDefinitions.VAR_VERSION, i18n.get("header.version"), 0.4F)); + return columnList; + + } + + private void setColumnProperties() { + final List columnList = getVisbleColumns(); + final List swColumnIds = new ArrayList<>(); + for (final TableColumn column : columnList) { + setColumnHeader(column.getColumnPropertyId(), column.getColumnHeader()); + setColumnExpandRatio(column.getColumnPropertyId(), column.getExpandRatio()); + swColumnIds.add(column.getColumnPropertyId()); + } + setVisibleColumns(swColumnIds.toArray()); + } + + private Map prepareQueryConfigFilters() { + final Map queryConfig = new HashMap<>(); + manageDistUIState.getManageDistFilters().getSearchText() + .ifPresent(value -> queryConfig.put(SPUIDefinitions.FILTER_BY_TEXT, value)); + + if (null != manageDistUIState.getManageDistFilters().getClickedDistSetType()) { + queryConfig.put(SPUIDefinitions.FILTER_BY_DISTRIBUTION_SET_TYPE, + manageDistUIState.getManageDistFilters().getClickedDistSetType()); + } + + queryConfig.put(SPUIDefinitions.FILTER_BY_DS_COMPLETE, Boolean.TRUE); + + return queryConfig; + } + + private void refreshDistributions() { + final LazyQueryContainer dsContainer = (LazyQueryContainer) getContainerDataSource(); + final int size = dsContainer.size(); + if (size < SPUIDefinitions.MAX_TABLE_ENTRIES) { + refreshTablecontainer(); + } + if (size != 0) { + setData(SPUIDefinitions.DATA_AVAILABLE); + } + } + + private Object getItemIdToSelect() { + if (manageDistUIState.getSelectedDistributions().isPresent()) { + return manageDistUIState.getSelectedDistributions().get(); + } + return null; + } + + private void selectRow() { + setValue(getItemIdToSelect()); + } + + private void refreshTablecontainer() { + final LazyQueryContainer dsContainer = (LazyQueryContainer) getContainerDataSource(); + dsContainer.refresh(); + selectRow(); + } + +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/DistributionSetSelectWindow.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/DistributionSetSelectWindow.java new file mode 100644 index 000000000..d1e77e529 --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/DistributionSetSelectWindow.java @@ -0,0 +1,307 @@ +/** + * Copyright (c) 2015 Bosch Software Innovations 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 + */ +package org.eclipse.hawkbit.ui.filtermanagement; + +import com.vaadin.server.FontAwesome; +import com.vaadin.ui.Alignment; +import com.vaadin.ui.Button; +import com.vaadin.ui.CheckBox; +import com.vaadin.ui.HorizontalLayout; +import com.vaadin.ui.Label; +import com.vaadin.ui.UI; +import com.vaadin.ui.VerticalLayout; +import com.vaadin.ui.Window; +import org.eclipse.hawkbit.repository.DistributionSetManagement; +import org.eclipse.hawkbit.repository.TargetFilterQueryManagement; +import org.eclipse.hawkbit.repository.TargetManagement; +import org.eclipse.hawkbit.repository.model.DistributionSet; +import org.eclipse.hawkbit.repository.model.TargetFilterQuery; +import org.eclipse.hawkbit.ui.common.CommonDialogWindow; +import org.eclipse.hawkbit.ui.common.DistributionSetIdName; +import org.eclipse.hawkbit.ui.common.builder.WindowBuilder; +import org.eclipse.hawkbit.ui.components.SPUIComponentProvider; +import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleNoBorderWithIcon; +import org.eclipse.hawkbit.ui.filtermanagement.event.CustomFilterUIEvent; +import org.eclipse.hawkbit.ui.utils.I18N; +import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; +import org.eclipse.hawkbit.ui.utils.SPUIDefinitions; +import org.springframework.beans.factory.annotation.Autowired; +import org.vaadin.spring.events.EventBus; + +import com.vaadin.data.Property; +import com.vaadin.server.Sizeable; +import com.vaadin.spring.annotation.SpringComponent; +import com.vaadin.spring.annotation.ViewScope; + +import java.io.Serializable; + +/** + * Creates a dialog window to select the distribution set for a target filter query. + */ +@SpringComponent +@ViewScope +public class DistributionSetSelectWindow + implements CommonDialogWindow.SaveDialogCloseListener, Property.ValueChangeListener { + + private static final long serialVersionUID = 4752345414134989396L; + + @Autowired + private I18N i18n; + + @Autowired + private DistributionSetSelectTable dsTable; + + @Autowired + private transient EventBus.SessionEventBus eventBus; + + @Autowired + private transient DistributionSetManagement distributionSetManagement; + + @Autowired + private transient TargetManagement targetManagement; + + @Autowired + private transient TargetFilterQueryManagement targetFilterQueryManagement; + + private CommonDialogWindow window; + private CheckBox checkBox; + private VerticalLayout verticalLayout; + private Long tfqId; + + private void init() { + Label label = new Label(i18n.get("label.auto.assign.description")); + + checkBox = new CheckBox(i18n.get("label.auto.assign.enable")); + checkBox.setId(UIComponentIdProvider.DIST_SET_SELECT_ENABLE_ID); + checkBox.setImmediate(true); + checkBox.addValueChangeListener(this); + + verticalLayout = new VerticalLayout(); + verticalLayout.addComponent(label); + verticalLayout.addComponent(checkBox); + verticalLayout.addComponent(dsTable); + + window = new WindowBuilder(SPUIDefinitions.CREATE_UPDATE_WINDOW) + .caption(i18n.get("caption.select.auto.assign.dist")).content(verticalLayout).layout(verticalLayout) + .i18n(i18n).saveDialogCloseListener(this).buildCommonDialogWindow(); + window.setId(UIComponentIdProvider.DIST_SET_SELECT_WINDOW_ID); + } + + public void setValue(DistributionSetIdName distSet) { + dsTable.setVisible(distSet != null); + checkBox.setValue(distSet != null); + dsTable.setValue(distSet); + dsTable.setCurrentPageFirstItemId(distSet); + } + + public DistributionSetIdName getValue() { + if (checkBox.getValue()) { + return (DistributionSetIdName) dsTable.getValue(); + } + return null; + } + + /** + * Shows a distribution set select window for the given target filter query + * + * @param tfqId + * target filter query id + */ + public void showForTargetFilter(Long tfqId) { + this.tfqId = tfqId; + TargetFilterQuery tfq = targetFilterQueryManagement.findTargetFilterQueryById(tfqId); + if(tfq == null) { + throw new IllegalStateException("TargetFilterQuery does not exist for the given id"); + } + + init(); + + DistributionSet distributionSet = tfq.getAutoAssignDistributionSet(); + if(distributionSet != null) { + setValue(DistributionSetIdName.generate(distributionSet)); + } else { + setValue(null); + } + + window.setWidth(40.0F, Sizeable.Unit.PERCENTAGE); + UI.getCurrent().addWindow(window); + window.setVisible(true); + } + + /** + * Is triggered when the checkbox value changes + * + * @param event + * change event + */ + @Override + public void valueChange(Property.ValueChangeEvent event) { + dsTable.setVisible(checkBox.getValue()); + if (window != null) { + window.center(); + + } + } + + /** + * Is triggered when the save button is clicked + * + * @return whether the click should be allowed + */ + @Override + public boolean canWindowSaveOrUpdate() { + return !checkBox.getValue() || dsTable.getValue() != null; + } + + /** + * Is called when the new value should be saved after the save button has + * been clicked + */ + @Override + public void saveOrUpdate() { + if(checkBox.getValue() && dsTable.getValue() != null) { + DistributionSetIdName ds = (DistributionSetIdName) dsTable.getValue(); + updateTargetFilterQueryDS(tfqId, ds.getId()); + + } else if(!checkBox.getValue()) { + updateTargetFilterQueryDS(tfqId, null); + + } + + } + + private void updateTargetFilterQueryDS(final Long targetFilterQueryId, final Long dsId) { + TargetFilterQuery tfq = targetFilterQueryManagement.findTargetFilterQueryById(targetFilterQueryId); + + + + if(dsId != null) { + confirmWithConsequencesDialog(tfq, dsId); + } else { + tfq.setAutoAssignDistributionSet(null); + targetFilterQueryManagement.updateTargetFilterQuery(tfq); + eventBus.publish(this, CustomFilterUIEvent.UPDATED_TARGET_FILTER_QUERY); + } + + + } + + private void confirmWithConsequencesDialog(TargetFilterQuery tfq, final Long dsId) { + + ConfirmConsequencesDialog dialog = new ConfirmConsequencesDialog(tfq, dsId, new ConfirmCallback() { + @Override + public void onConfirmResult(boolean accepted) { + if(accepted) { + tfq.setAutoAssignDistributionSet(distributionSetManagement.findDistributionSetById(dsId)); + targetFilterQueryManagement.updateTargetFilterQuery(tfq); + eventBus.publish(this, CustomFilterUIEvent.UPDATED_TARGET_FILTER_QUERY); + } + } + }); + + dialog.setWidth(40.0F, Sizeable.Unit.PERCENTAGE); + + UI.getCurrent().addWindow(dialog); + dialog.setVisible(true); + + } + + /** + * A dialog that displays how many targets will be assigned immediately with the + */ + private class ConfirmConsequencesDialog extends Window implements Button.ClickListener { + + private static final long serialVersionUID = 7738545414137389326L; + + private TargetFilterQuery targetFilterQuery; + private Long distributionSetId; + + private Button okButton; + private Button cancelButton; + + private ConfirmCallback callback; + + public ConfirmConsequencesDialog(TargetFilterQuery targetFilterQuery, final Long dsId, ConfirmCallback callback) { + super(i18n.get("caption.confirm.assign.consequences")); + + this.callback = callback; + this.targetFilterQuery = targetFilterQuery; + this.distributionSetId = dsId; + + init(); + + } + + private void init() { + setId(UIComponentIdProvider.DIST_SET_SELECT_CONS_WINDOW_ID); + setModal(true); + setResizable(false); + + VerticalLayout layout = new VerticalLayout(); + layout.setSpacing(true); + layout.setMargin(true); + setContent(layout); + + Long targetsCount = targetManagement.countTargetByTargetFilterQueryAndNonDS(distributionSetId, targetFilterQuery); + Label mainTextLabel; + if(targetsCount == 0) { + mainTextLabel = new Label(i18n.get("message.confirm.assign.consequences.none")); + } else { + mainTextLabel = new Label(i18n.get("message.confirm.assign.consequences.text", new Object[]{targetsCount})); + } + + layout.addComponent(mainTextLabel); + + HorizontalLayout buttonsLayout = new HorizontalLayout(); + buttonsLayout.setSizeFull(); + buttonsLayout.setSpacing(true); + buttonsLayout.addStyleName("actionButtonsMargin"); + layout.addComponent(buttonsLayout); + + + okButton = SPUIComponentProvider.getButton(UIComponentIdProvider.SAVE_BUTTON, i18n.get("button.ok"), "", "", true, + FontAwesome.SAVE, SPUIButtonStyleNoBorderWithIcon.class); + okButton.setSizeUndefined(); + okButton.addStyleName("default-color"); + okButton.addClickListener(this); + buttonsLayout.addComponent(okButton); + buttonsLayout.setComponentAlignment(okButton, Alignment.MIDDLE_RIGHT); + buttonsLayout.setExpandRatio(okButton, 1.0F); + + + cancelButton = SPUIComponentProvider.getButton(UIComponentIdProvider.CANCEL_BUTTON, i18n.get("button.cancel"), "", "", true, + FontAwesome.TIMES, SPUIButtonStyleNoBorderWithIcon.class); + cancelButton.setSizeUndefined(); + cancelButton.addStyleName("default-color"); + cancelButton.addClickListener(this); + buttonsLayout.addComponent(cancelButton); + buttonsLayout.setComponentAlignment(cancelButton, Alignment.MIDDLE_LEFT); + buttonsLayout.setExpandRatio(cancelButton, 1.0F); + + + } + + @Override + public void buttonClick(Button.ClickEvent event) { + if(event.getButton().getId().equals(okButton.getId())) { + callback.onConfirmResult(true); + } else { + callback.onConfirmResult(false); + } + + close(); + + } + } + + @FunctionalInterface + private interface ConfirmCallback extends Serializable { + void onConfirmResult(boolean accepted); + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TargetFilterBeanQuery.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TargetFilterBeanQuery.java index 9231b1658..c38dd694b 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TargetFilterBeanQuery.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TargetFilterBeanQuery.java @@ -13,8 +13,10 @@ import java.util.List; import java.util.Map; import org.eclipse.hawkbit.repository.TargetFilterQueryManagement; +import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.TargetFilterQuery; import org.eclipse.hawkbit.ui.common.UserDetailsFormatter; +import org.eclipse.hawkbit.ui.components.ProxyDistribution; import org.eclipse.hawkbit.ui.components.ProxyTargetFilter; import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil; import org.eclipse.hawkbit.ui.utils.SPDateTimeUtil; @@ -85,7 +87,7 @@ public class TargetFilterBeanQuery extends AbstractBeanQuery targetFilterQuery = getTargetFilterQueryManagement().findAllTargetFilterQuery( new PageRequest(startIndex / SPUIDefinitions.PAGE_SIZE, SPUIDefinitions.PAGE_SIZE, sort)); } else { - targetFilterQuery = getTargetFilterQueryManagement().findTargetFilterQueryByFilters( + targetFilterQuery = getTargetFilterQueryManagement().findTargetFilterQueryByName( new PageRequest(startIndex / SPUIDefinitions.PAGE_SIZE, SPUIDefinitions.PAGE_SIZE, sort), searchText); } @@ -98,6 +100,11 @@ public class TargetFilterBeanQuery extends AbstractBeanQuery proxyTarFilter.setModifiedDate(SPDateTimeUtil.getFormattedDate(tarFilterQuery.getLastModifiedAt())); proxyTarFilter.setLastModifiedBy(UserDetailsFormatter.loadAndFormatLastModifiedBy(tarFilterQuery)); proxyTarFilter.setQuery(tarFilterQuery.getQuery()); + + final DistributionSet distributionSet = tarFilterQuery.getAutoAssignDistributionSet(); + if (distributionSet != null) { + proxyTarFilter.setAutoAssignDistributionSet(new ProxyDistribution(distributionSet)); + } proxyTargetFilter.add(proxyTarFilter); } return proxyTargetFilter; @@ -117,7 +124,7 @@ public class TargetFilterBeanQuery extends AbstractBeanQuery .findAllTargetFilterQuery(new PageRequest(0, SPUIDefinitions.PAGE_SIZE, sort)); } else { firstPageTargetFilter = getTargetFilterQueryManagement() - .findTargetFilterQueryByFilters(new PageRequest(0, SPUIDefinitions.PAGE_SIZE, sort), searchText); + .findTargetFilterQueryByName(new PageRequest(0, SPUIDefinitions.PAGE_SIZE, sort), searchText); } final long size = firstPageTargetFilter.getTotalElements(); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TargetFilterTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TargetFilterTable.java index 40c28a40e..1e2d8a9e2 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TargetFilterTable.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TargetFilterTable.java @@ -17,9 +17,15 @@ import java.util.Map; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; +import com.vaadin.ui.Button; +import com.vaadin.ui.Link; +import com.vaadin.ui.Table; +import com.vaadin.ui.UI; +import org.eclipse.hawkbit.repository.DistributionSetManagement; import org.eclipse.hawkbit.repository.TargetFilterQueryManagement; import org.eclipse.hawkbit.repository.model.TargetFilterQuery; import org.eclipse.hawkbit.ui.common.ConfirmationDialog; +import org.eclipse.hawkbit.ui.components.ProxyDistribution; import org.eclipse.hawkbit.ui.components.SPUIComponentProvider; import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleSmallNoBorder; import org.eclipse.hawkbit.ui.filtermanagement.event.CustomFilterUIEvent; @@ -43,15 +49,11 @@ import com.vaadin.data.Item; import com.vaadin.server.FontAwesome; import com.vaadin.spring.annotation.SpringComponent; import com.vaadin.spring.annotation.ViewScope; -import com.vaadin.ui.Button; import com.vaadin.ui.Button.ClickEvent; -import com.vaadin.ui.Link; -import com.vaadin.ui.Table; -import com.vaadin.ui.UI; import com.vaadin.ui.themes.ValoTheme; /** - * + * Displays list of target filter queries * */ @SpringComponent @@ -75,6 +77,12 @@ public class TargetFilterTable extends Table { @Autowired private transient TargetFilterQueryManagement targetFilterQueryManagement; + @Autowired + private transient DistributionSetManagement distributionSetManagement; + + @Autowired + private DistributionSetSelectWindow dsSelectWindow; + private Container container; private static final int PROPERTY_DEPT = 3; @@ -141,15 +149,17 @@ public class TargetFilterTable extends Table { container.addContainerProperty(SPUILabelDefinitions.VAR_CREATED_DATE, Date.class, null); container.addContainerProperty(SPUILabelDefinitions.VAR_MODIFIED_DATE, Date.class, null); container.addContainerProperty(SPUILabelDefinitions.VAR_MODIFIED_BY, String.class, null); + container.addContainerProperty(SPUILabelDefinitions.AUTO_ASSIGN_DISTRIBUTION_SET, String.class, null); } private List getVisbleColumns() { final List columnList = new ArrayList<>(); columnList.add(new TableColumn(SPUILabelDefinitions.NAME, i18n.get("header.name"), 0.2F)); - columnList.add(new TableColumn(SPUILabelDefinitions.VAR_CREATED_USER, i18n.get("header.createdBy"), 0.15F)); + columnList.add(new TableColumn(SPUILabelDefinitions.VAR_CREATED_USER, i18n.get("header.createdBy"), 0.1F)); columnList.add(new TableColumn(SPUILabelDefinitions.VAR_CREATED_DATE, i18n.get("header.createdDate"), 0.2F)); - columnList.add(new TableColumn(SPUILabelDefinitions.VAR_MODIFIED_BY, i18n.get("header.modifiedBy"), 0.15F)); + columnList.add(new TableColumn(SPUILabelDefinitions.VAR_MODIFIED_BY, i18n.get("header.modifiedBy"), 0.1F)); columnList.add(new TableColumn(SPUILabelDefinitions.VAR_MODIFIED_DATE, i18n.get("header.modifiedDate"), 0.2F)); + columnList.add(new TableColumn(SPUILabelDefinitions.AUTO_ASSIGN_DISTRIBUTION_SET, i18n.get("header.auto.assignment.ds"), 0.1F)); columnList.add(new TableColumn(SPUIDefinitions.CUSTOM_FILTER_DELETE, i18n.get("header.delete"), 0.1F)); return columnList; @@ -171,7 +181,7 @@ public class TargetFilterTable extends Table { return deleteIcon; } - private String getDeleteIconId(final String targetFilterName) { + private static String getDeleteIconId(final String targetFilterName) { return new StringBuilder(UIComponentIdProvider.CUSTOM_FILTER_DELETE_ICON).append('.').append(targetFilterName) .toString(); } @@ -207,6 +217,9 @@ public class TargetFilterTable extends Table { addGeneratedColumn(SPUILabelDefinitions.NAME, (source, itemId, columnId) -> customFilterDetailButton((Long) itemId)); + addGeneratedColumn(SPUILabelDefinitions.AUTO_ASSIGN_DISTRIBUTION_SET, + (source, itemId, columnId) -> customFilterDistributionSetButton((Long) itemId)); + } private Button customFilterDetailButton(final Long itemId) { @@ -221,6 +234,34 @@ public class TargetFilterTable extends Table { return updateIcon; } + private Button customFilterDistributionSetButton(final Long itemId) { + final Item row1 = getItem(itemId); + final ProxyDistribution distSet = (ProxyDistribution) row1.getItemProperty(SPUILabelDefinitions.AUTO_ASSIGN_DISTRIBUTION_SET).getValue(); + final String buttonId = "distSetButton"; + Button updateIcon; + if(distSet == null) { + updateIcon = SPUIComponentProvider.getButton(buttonId, i18n.get("button.no.auto.assignment"), + i18n.get("button.auto.assignment.desc"), null, false, null, SPUIButtonStyleSmallNoBorder.class); + } else { + updateIcon = SPUIComponentProvider.getButton(buttonId, distSet.getNameVersion(), + i18n.get("button.auto.assignment.desc"), null, false, null, SPUIButtonStyleSmallNoBorder.class); + } + + updateIcon.addClickListener(this::onClickOfDistributionSetButton); + updateIcon.setData(row1); + updateIcon.addStyleName(ValoTheme.LINK_SMALL + " " + "on-focus-no-border link"); + + return updateIcon; + } + + private void onClickOfDistributionSetButton(final ClickEvent event) { + final Item item = (Item) ((Button) event.getComponent()).getData(); + final Long tfqId = (Long)item.getItemProperty(SPUILabelDefinitions.VAR_ID).getValue(); + + dsSelectWindow.showForTargetFilter(tfqId); + + } + private void onClickOfDetailButton(final ClickEvent event) { final String targetFilterName = (String) ((Button) event.getComponent()).getData(); final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/SPUIDefinitions.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/SPUIDefinitions.java index aae1da513..601b111ac 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/SPUIDefinitions.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/SPUIDefinitions.java @@ -481,6 +481,11 @@ public final class SPUIDefinitions { */ public static final String FILTER_BY_INVALID_QUERY = "FilterByInvalidFilterQueryText"; + /** + * Filter by distribution set complete. + */ + public static final String FILTER_BY_DS_COMPLETE = "FilterByDistributionSetComplete"; + /** * Sort order of column - created at in target table. */ diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/SPUILabelDefinitions.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/SPUILabelDefinitions.java index a7b8716cc..120c9fc58 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/SPUILabelDefinitions.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/SPUILabelDefinitions.java @@ -203,6 +203,10 @@ public final class SPUILabelDefinitions { * ASSIGNED DISTRIBUTION ID. */ public static final String ASSIGNED_DISTRIBUTION_ID = "assignedDistributionSet.id"; + /** + * AUTO ASSIGN DISTRIBUTION SET ID + */ + public static final String AUTO_ASSIGN_DISTRIBUTION_SET = "autoAssignDistributionSet"; /** * ASSIGNED DISTRIBUTION Name & Version. */ diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/UIComponentIdProvider.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/UIComponentIdProvider.java index e3475d20d..79ed125e1 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/UIComponentIdProvider.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/UIComponentIdProvider.java @@ -877,6 +877,26 @@ public final class UIComponentIdProvider { */ public static final String DOWNLOAD_ANONYMOUS_CHECKBOX = "downloadanonymouscheckbox"; + /** + * Distribution set select table id + */ + public static final String DIST_SET_SELECT_TABLE_ID = "distribution.set.select.table"; + + /** + * Distribution set select window id + */ + public static final String DIST_SET_SELECT_WINDOW_ID = "distribution.set.select.window"; + + /** + * Distribution set select consequences window id + */ + public static final String DIST_SET_SELECT_CONS_WINDOW_ID = "distribution.set.select.consequences.window"; + + /** + * Distribution set select enable checkbox id + */ + public static final String DIST_SET_SELECT_ENABLE_ID = "distribution.set.select.enable"; + /** * /* Private Constructor. */ diff --git a/hawkbit-ui/src/main/resources/VAADIN/themes/hawkbit/customstyles/table-common.scss b/hawkbit-ui/src/main/resources/VAADIN/themes/hawkbit/customstyles/table-common.scss index 2be45904a..ea5d5fbde 100644 --- a/hawkbit-ui/src/main/resources/VAADIN/themes/hawkbit/customstyles/table-common.scss +++ b/hawkbit-ui/src/main/resources/VAADIN/themes/hawkbit/customstyles/table-common.scss @@ -168,6 +168,7 @@ .v-table-cell-content { height: 27px; + font-size: 12px; } } diff --git a/hawkbit-ui/src/main/resources/messages.properties b/hawkbit-ui/src/main/resources/messages.properties index 6a68eeff8..eef0c3140 100644 --- a/hawkbit-ui/src/main/resources/messages.properties +++ b/hawkbit-ui/src/main/resources/messages.properties @@ -22,6 +22,8 @@ button.no.actions = No actions button.ok = OK button.cancel = Cancel button.upload.file = Upload File +button.no.auto.assignment = none +button.auto.assignment.desc = Select auto assign distribution set bulk.targets.upload = Please upload csv file. bulkupload.ds.name = DS Name @@ -59,6 +61,7 @@ caption.filter.simple = Simple Filter caption.filter.custom = Custom Filter caption.metadata = Metadata +caption.select.auto.assign.dist = Select auto assignment distribution set caption.add.softwaremodule = Configure Software Module caption.add.new.dist = Configure New Distribution caption.update.dist = Configure Update Distribution @@ -92,26 +95,27 @@ caption.confirm.abort.action = Confirm Abort Action caption.filter.delete.confirmbox = Confirm Filter Delete Action caption.metadata.popup = Metadata of caption.metadata.delete.action.confirmbox = Confirm Metadata Delete Action - +caption.confirm.assign.consequences = Auto assign consequences +caption.auto.assignment.ds = Auto assignment # Labels prefix with - label -label.dist.details.type = Type : -label.dist.details.name = Name : -label.dist.details.version = Version : -label.dist.details.vendor = Vendor : -label.dist.details.jvm = Runtime : -label.dist.details.ah = Application : -label.dist.details.os = OS : -label.modified.date = Last modified at : -label.modified.by = Last modified by : -label.created.at = Created at : -label.created.by = Created by : -label.target.count = Targets : -label.description = Description : -label.ip = Address : -label.type = Type : -label.assigned.type = Assignment type : -label.assigned.count = {0} Assigned +label.dist.details.type = Type : +label.dist.details.name = Name : +label.dist.details.version = Version : +label.dist.details.vendor = Vendor : +label.dist.details.jvm = Runtime : +label.dist.details.ah = Application : +label.dist.details.os = OS : +label.modified.date = Last modified at : +label.modified.by = Last modified by : +label.created.at = Created at : +label.created.by = Created by : +label.target.count = Targets : +label.description = Description : +label.ip = Address : +label.type = Type : +label.assigned.type = Assignment type : +label.assigned.count = {0} Assigned label.installed.count = {0} Installed label.mandatory.field = * Mandatory Field label.components.drop.area = Drop here to delete @@ -133,10 +137,6 @@ label.target.filter.count = Total Targets: label.target.filtered.total = Total filtered targets : label.filter.selected = Selected: label.filter.shown = Shown: -label.filter = Filter : -label.target.filter.count = Total Targets : -label.filter.selected = Selected : -label.filter.shown = Shown : label.filter.targets = Filtered targets : label.filter.status = Status, label.filter.tags = Tags, @@ -155,9 +155,9 @@ label.cancelling = Canceling label.retrieved = Retrieved label.download = Downloading label.unknown = Unknown -label.target.id = Controller Id : -label.target.ip = Controller IP : -label.target.security.token = Security token : +label.target.id = Controller Id : +label.target.ip = Controller IP : +label.target.security.token = Security token : label.filter.by.status = Filter by Status label.target.controller.attrs = Controller attributes label.target.lastpolldate = Last poll : @@ -168,9 +168,11 @@ label.configuration.auth.gatewaytoken = Allow a gateway to authenticate and mana label.configuration.auth.targettoken = Allow targets to authenticate directly with their target security token label.configuration.anonymous.download = Allow targets to download artifacts without security credentials label.unsupported.browser.ie=Sorry! current browser is not supported. Please use Internet Explorer 11 and above +label.auto.assign.description=When an auto assign distribution set is selected, it will be automatically assigned to all targets that match the target filter. +label.auto.assign.enable=Enable auto assignment # Checkbox label prefix with - checkbox -checkbox.dist.migration.required = Required Migration Step : +checkbox.dist.migration.required = Required Migration Step : checkbox.dist.required.migration.step = Required Migration Step # TextFields prefix with - textfield @@ -186,7 +188,7 @@ prompt.target.id = Controller ID #Tooltips prefix with - tooltip -tooltip.add.module = Add Software Module +tooltip.add.module = Add Software Module tooltip.status.unknown = Unknown tooltip.status.registered = Registered tooltip.status.pending = Pending @@ -240,7 +242,7 @@ message.software.type.discard.success = All software moduleTypes selected for de message.assign.software.discard.success = All software moduleTypes selected for assign are discarded successfully ! message.software.delete.success = All software modules selected for delete are deleted successfully ! message.software.type.delete.success = All software modules types selected for delete are deleted successfully ! -message.dist.set.type.deleted.success = {0} DistributionSetType deleted successfully ! +message.dist.set.type.deleted.success = {0} DistributionSetType deleted successfully ! message.new.dist.save.success = {0} - {1} saved successfully message.dist.update.success = {0} - {1} updated successfully message.duplicate.dist = Distribution set [{0}] or version [{1}] must be unique, entered value already exists. @@ -248,7 +250,7 @@ message.error.view = No such view: {0} message.accessdenied.view = No access to view: {0} message.no.data = No data available message.target.assignment = {0} Assignment(s) done -message.target.deleted = {0} Target(s) deleted +message.target.deleted = {0} Target(s) deleted message.dist.deleted = {0} Distribution set(s) deleted message.tag.update.mandatory = Please select the Tag to update message.tag.duplicate.check = {0} already exists, please enter another value @@ -274,7 +276,7 @@ message.error.ah.softmodule = Please select the Application to delete message.error.softmodule.deleted = The selected Software Module is already deleted message.cancel.action = Cancel.. message.cancel.action.success = Action cancelled successfully ! -message.cancel.action.failed = Unable to cancel the action ! +message.cancel.action.failed = Unable to cancel the action ! message.cancel.action.confirm = Are you sure that you want to cancel this action? message.target.alreadyAssigned = {0} Target(s) were already assigned message.dist.alreadyAssigned = {0} Distribution Set(s) were already assigned @@ -283,8 +285,8 @@ message.force.action.confirm = Are you sure that you want to force this action? message.force.action.success = Action forced successfully ! message.forcequit.action = Force Quit.. message.forcequit.action.success = Action has been force quit successfully ! -message.forcequit.action.failed = Force Quitting the action is not possible ! -message.forcequit.action.confirm = Attention!\nForce quit should only be used when the assignment action is not working properly.\nForce quitting an action has no effect on the connected target. It is just resetting \nthe data stored on the SP update server. \nAre you absolutely sure that you want to force quit this action? +message.forcequit.action.failed = Force Quitting the action is not possible ! +message.forcequit.action.confirm = Attention!\nForce quit should only be used when the assignment action is not working properly.\nForce quitting an action has no effect on the connected target. It is just resetting \nthe data stored on the SP update server. \nAre you absolutely sure that you want to force quit this action? message.distribution.no.update = distribution {0} set is already assigned to targets and cannot be changed message.action.not.allowed = Action not allowed message.action.did.not.work = Action did not work. Please try again. @@ -306,12 +308,14 @@ message.update.filter.success = Custom filter updated Successfully! message.target.filter.validation = Please enter name and query message.target.filter.duplicate = {0} already exists, please enter another value message.tag.use.bulk.upload = {0} cannot be deleted .It is in use in targets bulk upload -message.bulk.upload.tag.assignment.failed = Tag {0} assignment failed as tag no longer exists -message.bulk.upload.tag.assignments.failed= Few tag assignments failed as tags no longer exists +message.bulk.upload.tag.assignment.failed = Tag {0} assignment failed as tag no longer exists +message.bulk.upload.tag.assignments.failed= Few tag assignments failed as tags no longer exists +message.confirm.assign.consequences.none = This auto assignment will not have any effect on the currently available targets. In future added targets might match the filter and will receive the selected distribution set automatically. +message.confirm.assign.consequences.text = When you confirm this auto assignment, {0} targets which match the filter will immediately get assigned with the selected distribution set. # action info action.target.table.selectall = Select all (Ctrl+A) -action.target.table.clear = Clear selections +action.target.table.clear = Clear selections #reused messages soft.module.jvm =Runtime @@ -322,8 +326,8 @@ soft.module.os =OS message.error.noFileSelected = No file selected for upload message.error.noProvidedName = Please provide custom file name message.error.noSwModuleSelected = Please select a Software Module -message.no.duplicateFiles = Duplicate files selected -message.no.duplicateFile = Duplicate file selected : +message.no.duplicateFiles = Duplicate files selected +message.no.duplicateFile = Duplicate file selected : message.delete.artifact = Are you sure that you want to delete artifact {0} ? message.duplicate.filename = Duplicate file name message.swModule.deleted = {0} Software Module(s) deleted @@ -337,7 +341,7 @@ message.abort.upload = Are you sure that you want to abort the upload? upload.swModuleTable.header = Software Module -upload.selectedfile.name = file selected for upload +upload.selectedfile.name = file selected for upload upload.file.name = File name upload.sha1 = SHA1 checksum upload.md5 = MD5 checksum @@ -366,7 +370,6 @@ custom.created.date = Created Date #Manage distributions view label.drop.dist.delete.area = Drop here
to delete -label.no.tag.assigned = NO TAG caption.assign.software.dist.accordion.tab = Assign Software Modules message.software.assignment = {0} Software Module(s) Assignment(s) done message.dist.inuse = {0} Distribution is already assigned to target @@ -376,9 +379,7 @@ message.target.assigned = {0} is assigned to {1} message.dist.type.delete = {0} DistributionType(s) Deleted successfully. message.sw.module.type.delete = {0} Software Module Type(s) deleted successfully. message.dist.type.discard.success = All Distribution Types are discarded successfully ! -message.dist.discard.success = All Distributions are discarded successfully ! message.assign.discard.success = All assignments are discarded successfully ! -message.target.ds.assign.success = Assignments saved successfully ! message.bulk.upload.assignment.failed = Distribution set assignment failed as distribution set no longer exists! message.key.missing = Key is missing ! message.value.missing = Value is missing ! @@ -449,12 +450,12 @@ header.assigned.ds = Assigned DS header.installed.ds = Installed DS header.target.status = Status header.target.tags = Tags -header.distributionset = Distribution set -header.numberofgroups = No. of groups -header.detail.status = Detail status -header.total.targets = Total targets +header.total.targets = Total targets header.key = Key header.value = Value +header.auto.assignment.ds = Auto assignment +header.target.filter.name = Target filter name +header.target.filter.query = Target filter query distribution.details.header = Distribution set target.details.header = Target diff --git a/hawkbit-ui/src/main/resources/messages_de.properties b/hawkbit-ui/src/main/resources/messages_de.properties deleted file mode 100644 index e46be6b6e..000000000 --- a/hawkbit-ui/src/main/resources/messages_de.properties +++ /dev/null @@ -1,505 +0,0 @@ -# -# Copyright (c) 2015 Bosch Software Innovations 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 -# - -######################################################################################### -# This is the messages_en.properties file -######################################################################################### - -# Button names prefix with - button -button.save = Save -button.delete = Delete -button.discard = Discard -button.discard.all = Discard All -button.delete.all = Delete All -button.assign.all = Save Assign -button.actions = You have actions -button.no.actions = No actions -button.ok = OK -button.cancel = Cancel -button.upload.file = Upload File -bulk.targets.upload = Please upload csv file. -bulkupload.ds.name = DS Name - -# Headers prefix with - header -header.target.table=Targets -header.dist.table=Distributions -header.filter.tag=Filter by Tag -header.target.filter.tag=Filters -header.first.assignment.table = Targets -header.second.assignment.table = Distributions -header.dist.first.assignment.table = Distributions -header.dist.second.assignment.table = Software Modules -header.third.assignment.table = Discard -header.one.deletedist.table = Distribution Name -header.second.deletedist.table = Discard Changes -header.first.deletetarget.table = Target Name -header.second.deletetarget.table = Discard Changes -header.first.deleteswmodule.table = Delete software -header.first.delete.dist.type.table = DistributionSetType -header.second.delete.dist.type.table = Discard -header.first.delete.swmodule.type.table = Software Module Type -header.second.delete.swmodule.type.table = Discard -header.dist.twintable.selected=Selected -header.dist.twintable.available=Available -header.target.installed = Installed -header.target.assigned = Assigned - -# Captions prefix with - caption -caption.action.history = Action history for {0} -caption.error = Error -caption.new.softwaremodule.application = Configure New Application -caption.new.softwaremodule.jvm = Configure New Runtime -caption.new.softwaremodule.os = Configure New OS -caption.metadata = Metadata - - -caption.add.softwaremodule = Configure Software Module -caption.add.new.dist = Configure New Distribution -caption.update.dist = Configure Update Distribution -caption.add.tag = Configure Tag -caption.add.type = Configure Type -caption.add.new.target = Configure New Target -caption.update.target = Configure Update Target -caption.bulk.upload.targets = Bulk Upload -caption.softwares.distdetail.tab = Modules -caption.tags.tab = Tags -caption.logs.tab = Logs -caption.attributes.tab = Attributes -caption.types.tab = Types -caption.save.window = Action Details -caption.assign.dist.accordion.tab = Assign Distribution Set -caption.delete.dist.accordion.tab = Delete Distributions -caption.delete.target.accordion.tab = Delete Targets -caption.delete.swmodule.accordion.tab = Delete SW Modules -caption.delete.dist.set.type.accordion.tab = Delete Distribution Set Type -caption.delete.sw.module.type.accordion.tab = Delete Software Module Type -caption.attributes = Attributes -caption.panel.dist.installed = Installed Distribution Set -caption.panel.dist.assigned = Assigned Distribution Set -caption.soft.delete.confirmbox = Confirm Software Module Delete Action -caption.cancel.action.confirmbox = Confirm Action Cancellation -caption.forced.datefield = Force update at time -caption.force.action.confirmbox = Confirm Force Active Action -caption.filter.simple = Simple Filter -caption.filter.custom = Custom Filter -caption.filter.delete.confirmbox = Confirm Filter Delete Action -caption.confirm.abort.action = Confirm Abort Action - -caption.metadata.popup = Metadata of -caption.metadata.delete.action.confirmbox = Confirm Metadata Delete Action - -# Labels prefix with - label -label.dist.details.type = Type : -label.dist.details.name = Name : -label.dist.details.version = Version : -label.dist.details.vendor = Vendor : -label.dist.details.jvm = Runtime : -label.dist.details.ah = Application : -label.dist.details.os = OS : -label.modified.date = Last modified at : -label.modified.by = Last modified by : -label.created.at = Created at : -label.created.by = Created by : -label.target.count = Targets : -label.description = Description : -label.ip = Address : -label.type = Type : -label.assigned.type = Assignment type : -label.assigned.count = {0} Assigned -label.installed.count = {0} Installed -label.mandatory.field = * Mandatory Field -label.components.drop.area = Drop here to delete -label.software.module.drop.area = Delete Software -label.create.tag = Create Tag -label.update.tag = Update Tag -label.create.type = Create Type -label.update.type = Update Type -label.singleAssign.type = Firmware (FW) -label.multiAssign.type = Software (SW) -label.choose.type = Choose Type -label.choose.type.color = Choose Type Color -label.combobox.type = Select Type -label.combobox.tag = Select Tag -label.choose.tag = Choose Tag to update -label.choose.tag.color = Choose Tag Color -label.filter = Filter: -label.target.filter.count = Total Targets: -label.target.filtered.total = Total filtered targets : -label.filter.selected = Selected: -label.filter.shown = Shown: -label.filter = Filter : -label.target.filter.count = Total Targets : -label.filter.selected = Selected : -label.filter.shown = Shown : -label.filter.status = Status, -label.filter.tags = Tags, -label.filter.text = Search Text -label.filter.dist = Distribution, -label.filter.custom = Custom -label.target.filter.truncated={0} targets has been truncated in the list due the target size limit of {1}, use filters to reduce the targets to be shown -label.active =Active -label.inactive = In-active -label.finished = Finished -label.error = Error -label.warning = Warning -label.running = Running -label.cancelled = Cancelled -label.cancelling = Canceling -label.retrieved = Retrieved -label.download = Downloading -label.scheduled = Scheduled -label.target.id = Controller Id : -label.target.ip = Controller IP : -label.target.security.token = Security token : -label.filter.by.status = Filter by Status -label.target.controller.attrs = Controller attributes -label.target.lastpolldate = Last poll : -label.no.tag.assigned = NO TAG -label.tag.name = Tag name -label.configuration.auth.header = Allow targets to authenticate via a certificate authenticated by an reverse proxy -label.configuration.auth.gatewaytoken = Allow a gateway to authenticate and manage multiple targets through a gateway security token -label.configuration.auth.targettoken = Allow targets to authenticate directly with their target security token -label.configuration.anonymous.download = Allow targets to download artifacts without security credentials -label.unsupported.browser.ie=Sorry! current browser is not supported. Please use Internet Explorer 11 and above - -# Checkbox label prefix with - checkbox -checkbox.dist.migration.required = Required Migration Step : -checkbox.dist.required.migration.step = Required Migration Step - -# TextFields prefix with - textfield -textfield.name = Name -textfield.key = Key -textfield.version = Version -textfield.vendor = Vendor -textfield.description = Description -textfield.customfiltername = Filter name -textfield.value = Value -ui.version = Powered by Bosch IoT Software Provisioning -prompt.target.id = Controller ID - - -#Tooltips prefix with - tooltip -tooltip.add.module = Add Software Module -tooltip.status.unknown = Unknown -tooltip.status.registered = Registered -tooltip.status.pending = Pending -tooltip.status.error = Error -tooltip.status.insync = In-sync -tooltip.delete.module = Select and delete Software Module -tooltip.forced.item=Forced update action -tooltip.soft.item=Soft update action which interacts with an user as a attempt update action for example -tooltip.timeforced.item=Soft update until a specific time and then the action will be forced -tooltip.check.for.mandatory=Check to make Mandatory -tooltip.artifact.icon=Show Artifact Details -tooltip.click.to.edit = Click to edit -tooltip.metadata.icon = Manage Metadata - - -# Notification messages prefix with - message -message.save.success = {0} saved successfully -message.update.success = {0} updated successfully -message.delete.success = {0} deleted successfully -message.dist.installedorassigned = Target {targId} is already assigned/installed with distribution -message.dist.pending.action = Target {0} is already assigned with distribution {1} . Pending for action -message.empty.target.tags= No Tags Created -message.empty.disttype.tags = No Distribution type tags created -message.select.row = Please select a row to drag -message.error = Unknown error occured during the operation. Please contact administrator -message.dist.assigned.one = {0} is assigned to {1} -message.dist.assigned.many = {0} DistributionSets are assigned to {1} -message.dist.unassigned.one = {0} is unassigned from {1} -message.dist.unassigned.many = {0} DistributionSets are unassigned from {1} -message.target.assigned.one = {0} is assigned to {1} -message.target.assigned.many = {0} Targets are assigned to {1} -message.target.unassigned.one = {0} is unassigned from {1} -message.target.unassigned.many = {0} Targets are unassigned from {1} -message.target.assigned.pending = Some target(s) are already assigned.Pending for action -message.cannot.delete = Cannot be deleted -message.check.softwaremodule = Please provide both name and version! -message.cannot.delete.default.dstype = Default distribution set type cannot be deleted -message.duplicate.softwaremodule = {0} : {1} already exists! -message.cannot.delete.default.dstype = Default distribution set type cannot be deleted -message.tag.delete = Please unclick the tag {0}, then try to delete -message.dist.type.check.delete = Please unclick the distribution type {0}, then try to delete -message.swmodule.type.check.delete = Please unclick the Software Module type {0}, then try to delete -message.targets.already.deleted = Few Target(s) are already deleted.Pending for action -message.dists.already.deleted = Few distribution(s) are already deleted.Pending for action -message.target.deleted.pending = Target(s) already deleted.Pending for action -message.dist.deleted.pending = Distribution(s) already deleted.Pending for action -message.dist.delete.success = All selected distribution sets are deleted successfully ! -message.dist.discard.success = All distributions selected for delete are discarded successfully ! -message.target.delete.success = All selected targets are deleted successfully ! -message.target.discard.success = All targets selected for delete are discarded successfully ! -message.software.discard.success = All software modules selected for delete are discarded successfully ! -message.software.type.discard.success = All software moduleTypes selected for delete are discarded successfully ! -message.assign.software.discard.success = All software moduleTypes selected for assign are discarded successfully ! -message.software.delete.success = All software modules selected for delete are deleted successfully ! -message.software.type.delete.success = All software modules types selected for delete are deleted successfully ! -message.dist.set.type.deleted.success = {0} DistributionSetType deleted successfully ! -message.new.dist.save.success = {0} - {1} saved successfully -message.dist.update.success = {0} - {1} updated successfully -message.duplicate.dist = Distribution set [{0}] or version [{1}] must be unique, entered value already exists. -message.error.view = No such view: {0} -message.accessdenied.view = No access to view: {0} -message.no.data = No data available -message.target.assignment = {0} Assignment(s) done -message.target.deleted = {0} Target(s) deleted -message.dist.deleted = {0} Distribution Set(s) deleted -message.tag.update.mandatory = Please select the Tag to update -message.tag.duplicate.check = {0} already exists, please enter another value -message.type.key.duplicate.check = Distribution type with key {0} already exists, please give another value -message.type.key.swmodule.duplicate.check = Software Module type with key {0} already exists, please give another value -message.no.action.history = No action history is available for the target : {0} -message.no.available = --No messages available-- -message.no.actionupdateds.available = No other updates available for this action -message.mandatory.check = Mandatory details are missing -message.target.duplicate.check = Target [ {0} ] must be unique, entered value already exists. -message.permission.insufficient = Insufficient permissions to perform this action. -message.error.temp = The operation cannot be fulfilled due to {0}. Please contact administrator -message.dist.alreadyassigned = {0} : {1} is already assigned/installed, cannot be updated -message.dist.tag.alreadyassigned = {0} : {1} is already assigned/installed, cannot assign/un-assign to tag -message.dists.unassign.tag.alreadyassigned = Few of the DistributionSet's are already assigned to Target, those cannot be unassigned from Tag" -message.dists.assign.tag.alreadyassigned = Few of the DistributionSet's are already assigned to Target, those cannot be assigned to Tag" -message.dists.tag.assigned = {0} DistributionSet's assigned to Tag {1} -message.dists.tag.unassigned = {0} DistributionSet's un-assigned from Tag {1} -message.dist.no.operation = {0} - already assigned/installed, No operation -message.sm.delete.confirm = Are you sure that you want to delete the selected {0} Software Module? -message.error.os.softmodule = Please select the OS to delete -message.error.ah.softmodule = Please select the Application to delete -message.error.softmodule.deleted = The selected Software Module is already deleted -message.cancel.action = Cancel -message.cancel.action.success = Action cancelled successfully ! -message.cancel.action.failed = Unable to cancel the action ! -message.cancel.action.confirm = Are you sure that you want to cancel this action? -message.target.alreadyAssigned = {0} Target(s) were already assigned -message.dist.alreadyAssigned = {0} Distribution Set(s) were already assigned -message.force.action = Force -message.force.action.confirm = Are you sure that you want to force this action? -message.force.action.success = Action forced successfully ! -message.distribution.no.update = distribution {0} set is already assigned to targets and cannot be changed -message.action.not.allowed = Action not allowed -message.action.did.not.work = Action did not work. Please try again. -message.onlyone.distribution.assigned = Only one distribution set can be assigned -message.onlyone.distribution.dropallowed = Only one distribution set can be dropped -message.error.missing.typename = Missing Type Name -message.error.missing.typenameorkey = Missing Type Name or Key -message.tag.cannot.be.assigned = Target/DS cannot be assigned to {0} -message.no.targets.assiged.fortag = No targets are assigned to tag {0} -message.error.missing.tagname = Please select tag name -message.type.delete = Please unclick the distribution type {0}, then try to delete -message.error.dist.set.type.update= Distribution Set Type is already assigned to targets and cannot be changed -message.target.ds.assign.success = Assignments saved successfully ! -message.no.directory.upload = Directory upload is not supported - -message.delete.filter.confirm = Are you sure that you want to delete custom filter? -message.delete.filter.success = Custom filter {0} deleted Successfully! -message.create.filter.success = Custom filter {0} created Successfully! -message.update.filter.success = Custom filter updated Successfully! -message.target.filter.validation = Please enter name and query -message.target.filter.duplicate = {0} already exists, please enter another value -message.tag.use.bulk.upload = {0} cannot be deleted .It is in use in targets bulk upload -message.bulk.upload.tag.assignment.failed = Tag {0} assignment failed as tag no longer exists -message.bulk.upload.tag.assignments.failed= Few tag assignments failed as tags no longer exists - -#reused messages -soft.module.jvm =Runtime -soft.module.application =Application -soft.module.os =OS - -#Artifact upload -message.error.noFileSelected = No file selected for upload -message.error.noProvidedName = Please provide custom file name -message.error.noSwModuleSelected = Please select a Software Module -message.no.duplicateFiles = Duplicate files selected -message.no.duplicateFile = Duplicate file selected : -message.delete.artifact = Are you sure that you want to delete artifact {0} ? -message.duplicate.filename = Duplicate file name -message.swModule.deleted = {0} Software Module(s) deleted -message.error.missing.tagname = Please select tag name -message.upload.failed = Streaming Failed -message.uploadedfile.size.exceeded = File size exceeded .Allowed size {0} bytes -message.uploadedfile.aborted = File upload aborted -message.file.not.found = File not found -message.artifact.deleted =Artifact with file {0} deleted successfully -message.abort.upload = Are you sure that you want to abort the upload? - - - -upload.swModuleTable.header = Software Module -upload.selectedfile.name = file selected for upload -upload.file.name = File name -upload.sha1 = SHA1 checksum -upload.md5 = MD5 checksum -upload.last.modified.date=Last modified date -upload.failed = Failed -upload.success = Success -upload.caption.add.new.swmodule = Configure New Software Module -upload.caption.delete.swmodule = Configure Delete Software Module -upload.swmodule.type = Type -upload.artifact.alreadyExists = Artifact will be overridden as the given name already exists -upload.size = Size(B) -upload.validation = Validation -upload.reason = Reason -upload.action = Action -upload.result.status = Upload status -upload.file = Upload File -upload.caption.update.swmodule = Update Software Module -caption.tab.details = Details -caption.tab.description = Description - -caption.delete.artifact.confirmbox = Confirm Artifact Delete Action - -#Manage distributions view -label.drop.dist.delete.area = Drop here
to delete -caption.assign.software.dist.accordion.tab = Assign Software Modules -message.software.assignment = {0} Assignment(s) done -message.dist.inuse = {0} Distribution is already assigned to target -message.software.dist.already.assigned = {0} Distribution already has Software Module {1} -message.software.dist.type.notallowed = {0} Software Module type can not assign to Distribution {1} -message.target.assigned = {0} is assigned to {1} -message.dist.type.delete = {0} DistributionType(s) Deleted successfully. -message.sw.module.type.delete = {0} Software Module Type(s) deleted successfully. -message.dist.type.discard.success = All Distribution Types are discarded successfully ! -message.dist.discard.success = All Distributions are discarded successfully ! -message.assign.discard.success = All assignments are discarded successfully ! -message.bulk.upload.assignment.failed = Distribution set assignment failed as distribution set no longer exists! -message.key.missing = Key is missing ! -message.value.missing = Value is missing ! -message.metadata.saved = Metadata with key {0} successfully saved ! -message.metadata.updated = Metadata with key {0} successfully updated ! -message.metadata.duplicate.check = Metadata with key {0} already exists, please enter another value -message.metadata.deleted.successfully = Metadata with key {0} successfully deleted ! -message.confirm.delete.metadata = Are you sure that you want to delete metadata with key {0} ? -message.error.notification.ds.target.assigned = Distribution set {0}:{1} is already assigned to targets and cannot be changed - - -# Login view -notification.login.title=Welcome to Bosch IoT Software Provisioning. -notification.login.description=Please login with your Bosch Identity Management credentials. -notification.login.failed.title=Login failed! -notification.login.failed.description=Login with the given credentials failed. -notification.login.failed.credentialsexpired.title=Passwort Abgelaufen! -notification.login.failed.credentialsexpired.description=Passwort ist entweder abgelaufen or muss initial gesetzt werden, bitte im Benutzer Verwaltungssystem �ndern. -label.login.tenant=Tenant -label.login.username=Username -label.login.password=Password -button.login.signin=Sign in -checkbox.login.rememberme=Remember me - -# Links -link.documentation.name=Dokumentation -link.demo.name=Demo -link.requestaccount.name=Beantragung eines Accounts -link.support.name=Support -link.usermanagement.name=Benutzerverwaltung - - -# System Configuration View -notification.configuration.save=Ver�nderungen gespeichert -configuration.defaultdistributionset.title=Distribution Set Typ -configuration.defaultdistributionset.select.label=Wahl des default Distribution Set typs: -configuration.savebutton.tooltip=Konfigurationen speichern -configuration.cancellbutton.tooltip=Konfigurationen zur�cksetzen -configuration.authentication.title=Authentifikationseinstellungen - -#Calendar -calendar.year=Jahr -calendar.years=Jahre -calendar.month=Monat -calendar.months=Monate -calendar.day=Tag -calendar.days=Tage -calendar.hour=Stunde -calendar.hours=Stunden -calendar.minute=Minute -calendar.minutes=Minuten -calendar.second=Sekunde -calendar.seconds=Sekunden - -header.name = Name -header.vendor = Vendor -header.version = Version -header.description = Description -header.createdBy = Created By -header.createdDate = Created Date -header.modifiedBy = Modified By -header.modifiedDate = Modified Date -header.delete = Delete -header.assigned.ds = Assigned DS -header.installed.ds = Installed DS -header.status = Status -header.target.tags = Tags -header.distributionset = Distribution set -header.numberofgroups = No. of groups -header.detail.status = Detail status -header.total.targets = Total targets -header.type = Type -header.swmodules = SwModules -header.migrations.step=IsRequiredMigrationStep - -header.action=Actions -header.action.run=Run -header.action.pause=Pause -header.action.update=Edit - -header.rolloutgroup.installed.percentage = % Finished -header.rolloutgroup.threshold.error = Error threshold -header.rolloutgroup.threshold = Trigger threshold - -header.rolloutgroup.target.date = Date and time -header.rolloutgroup.target.message = Messages - -rollout.group.label.target.truncated = {0} targets has been truncated in the list due the target size limit of {1} - -distribution.details.header = Distribution Set -target.details.header = Target -header.caption.mandatory = Mandatory -header.caption.typename = SoftwareModuleType -header.caption.softwaremodule = SoftwareModule -header.caption.unassign = Unassign -message.sw.unassigned = Software Module {0} successfully unassigned -header.caption.upload.details = Upload details -header.key = Key -header.value = Value -combo.type.tag.name = Type tag name - -label.yes = Yes -label.no =No - - -#Menu -menu.title = Software Provisioning - - -#Rollout management -prompt.number.of.groups = Number of groups -prompt.tigger.threshold = Trigger threshold -prompt.error.threshold = Error threshold -prompt.distribution.set = Distribution Set -caption.configure.rollout = Configure Rollout -caption.update.rollout = Update Rollout -prompt.target.filter = Custom Target Filter -message.rollout.nonzero.group.number = Number of groups must be greater than zero -message.rollout.max.group.number = Number of groups must not be greater than 500 -message.rollout.duplicate.check = Rollout [ {0} ] must be unique, entered value already exists. -message.rollout.field.value.range = Value should be in range {0} to {1} -message.correct.invalid.value = Please correct invalid values -message.enter.number = Please enter number -message.rollout.started = Rollout {0} started successfully -message.rollout.paused = Rollout {0} paused successfully -message.rollout.resumed = Rollout {0} resumed successfully -message.rollout.noofgroups.or.targetfilter.missing = Please enter number of groups and select target filter -message.rollouts = Rollouts -label.target.per.group = Targets per group : -message.dist.already.assigned = Distribution {0} is already assigned to target -message.error.creating.rollout = Server error. Error creating Rollout. Please contact the administrator -message.error.starting.rollout = Server error. Error starting Rollout. Please contact the administrator - -#Target Filter Management -breadcrumb.target.filter.custom.filters = Custom Filters diff --git a/hawkbit-ui/src/main/resources/messages_en.properties b/hawkbit-ui/src/main/resources/messages_en.properties deleted file mode 100644 index 4328b83f8..000000000 --- a/hawkbit-ui/src/main/resources/messages_en.properties +++ /dev/null @@ -1,501 +0,0 @@ -# -# Copyright (c) 2015 Bosch Software Innovations 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 -# - -######################################################################################### -# This is the messages_en.properties file -######################################################################################### - -# Button names prefix with - button -button.save = Save -button.delete = Delete -button.discard = Discard -button.discard.all = Discard All -button.delete.all = Delete All -button.assign.all = Save Assign -button.actions = You have actions -button.no.actions = No actions -button.ok = OK -button.cancel = Cancel -button.upload.file = Upload File -bulk.targets.upload = Please upload csv file. -bulkupload.ds.name = DS Name - -# Headers prefix with - header -header.target.table=Targets -header.dist.table=Distributions -header.filter.tag=Filter by Tag -header.target.filter.tag=Filters -header.first.assignment.table = Targets -header.second.assignment.table = Distributions -header.dist.first.assignment.table = Distributions -header.dist.second.assignment.table = Software Modules -header.third.assignment.table = Discard -header.one.deletedist.table = Distribution Name -header.second.deletedist.table = Discard Changes -header.first.deletetarget.table = Target Name -header.second.deletetarget.table = Discard Changes -header.first.deleteswmodule.table = Delete software -header.first.delete.dist.type.table = DistributionSetType -header.second.delete.dist.type.table = Discard -header.first.delete.swmodule.type.table = Software Module Type -header.second.delete.swmodule.type.table = Discard -header.dist.twintable.selected=Selected -header.dist.twintable.available=Available -header.target.installed = Installed -header.target.assigned = Assigned - -# Captions prefix with - caption -caption.action.history = Action history for {0} -caption.error = Error -caption.new.softwaremodule.application = Configure New Application -caption.new.softwaremodule.jvm = Configure New Runtime -caption.new.softwaremodule.os = Configure New OS -caption.filter.simple = Simple Filter -caption.filter.custom = Custom Filter -caption.metadata = Metadata - -caption.add.softwaremodule = Configure Software Module -caption.add.new.dist = Configure New Distribution -caption.update.dist = Configure Update Distribution -caption.add.tag = Configure Tag -caption.add.type = Configure Type -caption.add.new.target = Configure New Target -caption.update.target = Configure Update Target -caption.bulk.upload.targets = Bulk Upload -caption.softwares.distdetail.tab = Modules -caption.tags.tab = Tags -caption.logs.tab = Logs -caption.attributes.tab = Attributes -caption.types.tab = Types -caption.save.window = Action Details -caption.assign.dist.accordion.tab = Assign Software Module -caption.delete.dist.accordion.tab = Delete Distributions -caption.delete.target.accordion.tab = Delete Targets -caption.delete.swmodule.accordion.tab = Delete SW Modules -caption.delete.dist.set.type.accordion.tab = Delete Distribution Set Type -caption.delete.sw.module.type.accordion.tab = Delete Software Module Type -caption.attributes = Attributes -caption.panel.dist.installed = Installed Distribution Set -caption.panel.dist.assigned = Assigned Distribution Set -caption.soft.delete.confirmbox = Confirm Software Module Delete Action -caption.cancel.action.confirmbox = Confirm Action Cancellation -caption.forced.datefield = Force update at time -caption.force.action.confirmbox = Confirm Force Active Action -caption.filter.delete.confirmbox = Confirm Filter Delete Action -caption.metadata.popup = Metadata of -caption.metadata.delete.action.confirmbox = Confirm Metadata Delete Action - -caption.confirm.abort.action = Confirm Abort Action - -# Labels prefix with - label -label.dist.details.type = Type : -label.dist.details.name = Name : -label.dist.details.version = Version : -label.dist.details.vendor = Vendor : -label.dist.details.jvm = Runtime : -label.dist.details.ah = Application : -label.dist.details.os = OS : -label.modified.date = Last modified at : -label.modified.by = Last modified by : -label.created.at = Created at : -label.created.by = Created by : -label.target.count = Targets : -label.description = Description : -label.ip = Address : -label.type = Type : -label.assigned.type = Assignment type : -label.assigned.count = {0} Assigned -label.installed.count = {0} Installed -label.mandatory.field = * Mandatory Field -label.components.drop.area = Drop here to delete -label.software.module.drop.area = Delete Software -label.create.tag = Create Tag -label.update.tag = Update Tag -label.create.type = Create Type -label.update.type = Update Type -label.singleAssign.type = Firmware (FW) -label.multiAssign.type = Software (SW) -label.choose.type = Choose Type to Update -label.choose.type.color = Choose Type Color -label.combobox.type = Select Type -label.combobox.tag = Select Tag -label.choose.tag = Choose Tag to update -label.choose.tag.color = Choose Tag Color -label.filter = Filter: -label.target.filter.count = Total Targets: -label.target.filtered.total = Total filtered targets : -label.filter.selected = Selected: -label.filter.shown = Shown: -label.filter.targets = Filtered targets : -label.filter = Filter : -label.target.filter.count = Total Targets : -label.filter.selected = Selected : -label.filter.shown = Shown : -label.filter.status = Status, -label.filter.tags = Tags, -label.filter.text = Search Text -label.filter.dist = Distribution, -label.filter.custom = Custom -label.target.filter.truncated={0} targets has been truncated in the list due the target size limit of {1}, use filters to reduce the targets to be shown -label.active =Active -label.inactive = In-active -label.finished = Finished -label.error = Error -label.warning = Warning -label.running = Running -label.cancelled = Cancelled -label.cancelling = Canceling -label.retrieved = Retrieved -label.download = Downloading -label.scheduled = Scheduled -label.target.id = Controller Id : -label.target.ip = Controller IP : -label.target.security.token = Security token : -label.filter.by.status = Filter by Status -label.target.controller.attrs = Controller attributes -label.target.lastpolldate = Last poll : -label.no.tag.assigned = NO TAG -label.tag.name = Tag name -label.configuration.auth.header = Allow targets to authenticate via a certificate authenticated by an reverse proxy -label.configuration.auth.gatewaytoken = Allow a gateway to authenticate and manage multiple targets through a gateway security token -label.configuration.auth.targettoken = Allow targets to authenticate directly with their target security token -label.configuration.anonymous.download = Allow targets to download artifacts without security credentials -label.unsupported.browser.ie=Sorry! current browser is not supported. Please use Internet Explorer 11 and above - -# Checkbox label prefix with - checkbox -checkbox.dist.migration.required = Required Migration Step : -checkbox.dist.required.migration.step = Required Migration Step - -# TextFields prefix with - textfield -textfield.name = Name -textfield.key = Key -textfield.version = Version -textfield.vendor = Vendor -textfield.description = Description -textfield.customfiltername = Filter name -textfield.value = Value -ui.version = Powered by Bosch IoT Software Provisioning -prompt.target.id = Controller ID - - -#Tooltips prefix with - tooltip -tooltip.add.module = Add Software Module -tooltip.status.unknown = Unknown -tooltip.status.registered = Registered -tooltip.status.pending = Pending -tooltip.status.error = Error -tooltip.status.insync = In-sync -tooltip.delete.module = Select and delete Software Module -tooltip.forced.item=Forced update action -tooltip.soft.item=Soft update action which interacts with an user as a attempt update action for example -tooltip.timeforced.item=Soft update until a specific time and then the action will be forced -tooltip.check.for.mandatory=Check to make Mandatory -tooltip.artifact.icon=Show Artifact Details -tooltip.click.to.edit = Click to edit -tooltip.metadata.icon = Manage Metadata - - -# Notification messages prefix with - message -message.save.success = {0} saved successfully -message.update.success = {0} updated successfully -message.delete.success = {0} deleted successfully -message.dist.installedorassigned = Target {targId} is already assigned/installed with distribution -message.dist.pending.action = Target {0} is already assigned with distribution {1} . Pending for action -message.empty.target.tags= No Tags Created -message.empty.disttype.tags = No Distribution type tags created -message.select.row = Please select a row to drag -message.error = Unknown error occured during the operation. Please contact administrator -message.dist.assigned.one = {0} is assigned to {1} -message.dist.assigned.many = {0} DistributionSets are assigned to {1} -message.dist.unassigned.one = {0} is unassigned from {1} -message.dist.unassigned.many = {0} DistributionSets are unassigned from {1} -message.target.assigned.one = {0} is assigned to {1} -message.target.assigned.many = {0} Targets are assigned to {1} -message.target.unassigned.one = {0} is unassigned from {1} -message.target.unassigned.many = {0} Targets are unassigned from {1} -message.target.assigned.pending = Some target(s) are already assigned.Pending for action -message.cannot.delete = Cannot be deleted -message.check.softwaremodule = Please provide both name and version! -message.duplicate.softwaremodule = {0} : {1} already exists! -message.tag.delete = Please unclick the tag {0}, then try to delete -message.dist.type.check.delete = Please unclick the distribution type {0}, then try to delete -message.swmodule.type.check.delete = Please unclick the Software Module type {0}, then try to delete -message.targets.already.deleted = Few Target(s) are already deleted.Pending for action -message.dists.already.deleted = Few distribution(s) are already deleted.Pending for action -message.target.deleted.pending = Target(s) already deleted.Pending for action -message.dist.deleted.pending = Distribution(s) already deleted.Pending for action -message.dist.delete.success = All selected distribution sets are deleted successfully ! -message.dist.discard.success = All distributions selected for delete are discarded successfully ! -message.target.delete.success = All selected targets are deleted successfully ! -message.target.discard.success = All targets selected for delete are discarded successfully ! -message.software.discard.success = All software modules selected for delete are discarded successfully ! -message.software.type.discard.success = All software moduleTypes selected for delete are discarded successfully ! -message.assign.software.discard.success = All software moduleTypes selected for assign are discarded successfully ! -message.software.delete.success = All software modules selected for delete are deleted successfully ! -message.software.type.delete.success = All software modules types selected for delete are deleted successfully ! -message.dist.set.type.deleted.success = {0} DistributionSetType deleted successfully ! -message.new.dist.save.success = {0} - {1} saved successfully -message.dist.update.success = {0} - {1} updated successfully -message.duplicate.dist = Distribution set [{0}] or version [{1}] must be unique, entered value already exists. -message.error.view = No such view: {0} -message.accessdenied.view = No access to view: {0} -message.no.data = No data available -message.target.assignment = {0} Assignment(s) done -message.target.deleted = {0} Target(s) deleted -message.dist.deleted = {0} Distribution set(s) deleted -message.tag.update.mandatory = Please select the Tag to update -message.tag.duplicate.check = {0} already exists, please enter another value -message.type.key.duplicate.check = Distribution type with key {0} already exists, please give another value -message.type.key.swmodule.duplicate.check = Software Module type with key {0} already exists, please give another value -message.no.action.history = No action history is available for the target : {0} -message.no.available = --No messages available-- -message.no.actionupdateds.available = No other updates available for this action -message.mandatory.check = Mandatory details are missing -message.target.duplicate.check = Target [ {0} ] must be unique, entered value already exists. -message.permission.insufficient = Insufficient permissions to perform this action. -message.error.temp = The operation cannot be fulfilled due to {0}. Please contact administrator -message.dist.alreadyassigned = {0} : {1} is already assigned/installed, cannot be updated -message.dist.tag.alreadyassigned = {0} : {1} is already assigned/installed, cannot assign/un-assign to tag -message.dists.unassign.tag.alreadyassigned = Few of the DistributionSet's are already assigned to Target, those cannot be unassigned from Tag" -message.dists.assign.tag.alreadyassigned = Few of the DistributionSet's are already assigned to Target, those cannot be assigned to Tag" -message.dists.tag.assigned = {0} DistributionSet's assigned to Tag {1} -message.dists.tag.unassigned = {0} DistributionSet's un-assigned from Tag {1} -message.dist.no.operation = {0} - already assigned/installed, No operation -message.sm.delete.confirm = Are you sure that you want to delete the selected {0} Software Module? -message.error.os.softmodule = Please select the OS to delete -message.error.ah.softmodule = Please select the Application to delete -message.error.softmodule.deleted = The selected Software Module is already deleted -message.cancel.action = Cancel -message.cancel.action.success = Action cancelled successfully ! -message.cancel.action.failed = Unable to cancel the action ! -message.cancel.action.confirm = Are you sure that you want to cancel this action? -message.target.alreadyAssigned = {0} Target(s) were already assigned -message.dist.alreadyAssigned = {0} Distribution Set(s) were already assigned -message.force.action = Force -message.force.action.confirm = Are you sure that you want to force this action? -message.force.action.success = Action forced successfully ! -message.distribution.no.update = distribution {0} set is already assigned to targets and cannot be changed -message.action.not.allowed = Action not allowed -message.action.did.not.work = Action did not work. Please try again. -message.onlyone.distribution.assigned = Only one distribution set can be assigned -message.onlyone.distribution.dropallowed = Only one distribution set can be dropped -message.error.missing.typename = Missing Type Name -message.error.missing.typenameorkey = Missing Type Name or Key or Software Module type -message.tag.cannot.be.assigned = Target/DS cannot be assigned to {0} -message.no.targets.assiged.fortag = No targets are assigned to tag {0} -message.error.missing.tagname = Please select tag name -message.type.delete = Please unclick the distribution type {0}, then try to delete -message.error.dist.set.type.update= Distribution Set Type is already assigned to targets and cannot be changed -message.target.ds.assign.success = Assignments saved successfully ! -message.no.directory.upload = Directory upload is not supported - -message.delete.filter.confirm = Are you sure that you want to delete custom filter? -message.delete.filter.success = Custom filter {0} deleted Successfully! -message.create.filter.success = Custom filter {0} created Successfully! -message.update.filter.success = Custom filter updated Successfully! -message.target.filter.validation = Please enter name and query -message.target.filter.duplicate = {0} already exists, please enter another value - -# action info -action.target.table.selectall = Select all (Ctrl+A) -action.target.table.clear = Clear selections - -#reused messages -soft.module.jvm =Runtime -soft.module.application =Application -soft.module.os =OS - -#Artifact upload -message.error.noFileSelected = No file selected for upload -message.error.noProvidedName = Please provide custom file name -message.error.noSwModuleSelected = Please select a Software Module -message.no.duplicateFiles = Duplicate files selected -message.no.duplicateFile = Duplicate file selected : -message.delete.artifact = Are you sure that you want to delete artifact {0} ? -message.duplicate.filename = Duplicate file name -message.swModule.deleted = {0} Software Module(s) deleted -message.upload.failed = Streaming Failed -message.uploadedfile.size.exceeded = File size exceeded .Allowed size {0} bytes -message.uploadedfile.aborted = File upload aborted -message.file.not.found = File not found -message.abort.upload = Are you sure that you want to abort the upload? - - -upload.swModuleTable.header = Software Module -upload.selectedfile.name = file selected for upload -upload.file.name = File name -upload.sha1 = SHA1 checksum -upload.md5 = MD5 checksum -upload.last.modified.date=Last modified date -upload.failed = Failed -upload.success = Success -upload.caption.add.new.swmodule = Configure New Software Module -upload.caption.delete.swmodule = Configure Delete Software Module -upload.swmodule.type = Type -upload.artifact.alreadyExists = Artifact will be overridden as the given name already exists -upload.size = Size(B) -upload.validation = Validation -upload.reason = Reason -upload.action = Action -upload.result.status = Upload status -upload.file = Upload File -upload.caption.update.swmodule = Update Software Module -caption.tab.details = Details -caption.tab.description = Description - -caption.delete.artifact.confirmbox = Confirm Artifact Delete Action - - -#Manage distributions view -label.drop.dist.delete.area = Drop here
to delete -caption.assign.software.dist.accordion.tab = Assign Software Modules -message.software.assignment = {0} Software Module(s) Assignment(s) done -message.dist.inuse = {0} Distribution is already assigned to target -message.software.dist.already.assigned = {0} Distribution already has Software Module {1} -message.software.dist.type.notallowed = {0} Software Module type can not assign to Distribution {1} -message.target.assigned = {0} is assigned to {1} -message.dist.type.delete = {0} DistributionType(s) Deleted successfully. -message.sw.module.type.delete = {0} Software Module Type(s) deleted successfully. -message.dist.type.discard.success = All Distribution Types are discarded successfully ! -message.dist.discard.success = All Distributions are discarded successfully ! -message.assign.discard.success = All assignments are discarded successfully ! -message.key.missing = Key is missing ! -message.value.missing = Value is missing ! -message.metadata.saved = Metadata with key {0} successfully saved ! -message.metadata.updated = Metadata with key {0} successfully updated ! -message.metadata.duplicate.check = Metadata with key {0} already exists, please enter another value -message.metadata.deleted.successfully = Metadata with key {0} successfully deleted ! -message.confirm.delete.metadata = Are you sure that you want to delete metadata with key {0} ? -message.error.notification.ds.target.assigned = Distribution set {0}:{1} is already assigned to targets and cannot be changed - -# Login view -notification.login.title=Welcome to Bosch IoT Software Provisioning. -notification.login.description=Please login with your Bosch Identity Management credentials. -notification.login.failed.title=Login failed! -notification.login.failed.description=Login with the given credentials failed. -notification.login.failed.credentialsexpired.title=Password Expired! -notification.login.failed.credentialsexpired.description=Password has been expired or needs to be set initially, please visit the User Management to change or set your password. -label.login.tenant=Tenant -label.login.username=Username -label.login.password=Password -button.login.signin=Sign in -checkbox.login.rememberme=Remember me - -# Links -link.documentation.name=Documentation -link.demo.name=Demo -link.requestaccount.name=Request Account -link.support.name=Support -link.usermanagement.name=User Management - -# System Configuration View -notification.configuration.save=Saved changes -configuration.defaultdistributionset.title=Distribution Set Type -configuration.defaultdistributionset.select.label=Select the default Distribution Set type: -configuration.savebutton.tooltip=Save Configurations -configuration.cancellbutton.tooltip=Cancel Configurations -configuration.authentication.title=Authentication Configuration -controller.polling.title=Polling Configuration -controller.polling.time=Polling Time -controller.polling.overduetime=Polling Overdue Time - -#Calendar -calendar.year=year -calendar.years=years -calendar.month=month -calendar.months=months -calendar.day=day -calendar.days=days -calendar.hour=hour -calendar.hours=hours -calendar.minute=minute -calendar.minutes=minutes -calendar.second=second -calendar.seconds=seconds - -header.name = Name -header.vendor = Vendor -header.version = Version -header.description = Description -header.createdBy = Created By -header.createdDate = Created Date -header.modifiedBy = Modified By -header.modifiedDate = Modified Date -header.delete = Delete -header.assigned.ds = Assigned DS -header.installed.ds = Installed DS -header.status = Status -header.target.tags = Tags -header.distributionset = Distribution Set -header.numberofgroups = No. of groups -header.detail.status = Detail status -header.total.targets = Total targets -header.type = Type -header.swmodules = SwModules -header.migrations.step=IsRequiredMigrationStep - -header.action=Actions -header.action.run=Run -header.action.pause=Pause -header.action.update=Edit - -header.rolloutgroup.installed.percentage = % Finished -header.rolloutgroup.threshold.error = Error threshold -header.rolloutgroup.threshold = Trigger threshold - - - -header.rolloutgroup.target.date = Date and time -header.rolloutgroup.target.message = Messages - -rollout.group.label.target.truncated = {0} targets has been truncated in the list due the target size limit of {1} - -distribution.details.header = Distribution Set -target.details.header = Target -header.caption.mandatory = Mandatory -header.caption.typename = SoftwareModuleType -header.caption.softwaremodule = SoftwareModule -header.caption.upload.details = Upload details -header.key = Key -header.value = Value -combo.type.tag.name = Type tag name - -label.yes = Yes -label.no =No - -#Menu -menu.title = Software Provisioning - - -#Rollout management -prompt.number.of.groups = Number of groups -prompt.tigger.threshold = Trigger threshold -prompt.error.threshold = Error threshold -prompt.distribution.set = Distribution Set -caption.configure.rollout = Configure Rollout -caption.update.rollout = Update Rollout -prompt.target.filter = Custom Target Filter -message.rollout.nonzero.group.number = Number of groups must be greater than zero -message.rollout.max.group.number = Number of groups must not be greater than 500 -message.rollout.duplicate.check = Rollout [ {0} ] must be unique, entered value already exists. -message.correct.invalid.value = Please correct invalid values -message.enter.number = Please enter number -message.rollout.field.value.range = Value should be in range {0} to {1} -message.rollout.started = Rollout {0} started successfully -message.rollout.paused = Rollout {0} paused successfully -message.rollout.resumed = Rollout {0} resumed successfully -message.rollout.noofgroups.or.targetfilter.missing = Please enter number of groups and select target filter -message.rollouts = Rollouts -label.target.per.group = Targets per group : -message.dist.already.assigned = Distribution {0} is already assigned to target -message.error.creating.rollout = Server error. Error creating Rollout. Please contact the administrator -message.error.starting.rollout = Server error. Error starting Rollout. Please contact the administrator - -#Target Filter Management -breadcrumb.target.filter.custom.filters = Custom Filters From 9bf4692497eb6d60b2b2a4d039b541e72524e9d0 Mon Sep 17 00:00:00 2001 From: Dominik Herbst Date: Fri, 23 Sep 2016 13:42:37 +0200 Subject: [PATCH 07/28] Corrected copyright headers Signed-off-by: Dominik Herbst --- .../json/model/targetfilter/MgmtTargetFilterQuery.java | 7 ++++++- .../targetfilter/MgmtTargetFilterQueryRequestBody.java | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/targetfilter/MgmtTargetFilterQuery.java b/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/targetfilter/MgmtTargetFilterQuery.java index c5378a195..99ba58897 100644 --- a/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/targetfilter/MgmtTargetFilterQuery.java +++ b/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/targetfilter/MgmtTargetFilterQuery.java @@ -1,5 +1,10 @@ /** - * Copyright (c) 2011-2015 Bosch Software Innovations GmbH, Germany. All rights reserved. + * Copyright (c) 2015 Bosch Software Innovations 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 */ package org.eclipse.hawkbit.mgmt.json.model.targetfilter; diff --git a/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/targetfilter/MgmtTargetFilterQueryRequestBody.java b/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/targetfilter/MgmtTargetFilterQueryRequestBody.java index a87c6fd2f..8cbc87d2e 100644 --- a/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/targetfilter/MgmtTargetFilterQueryRequestBody.java +++ b/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/targetfilter/MgmtTargetFilterQueryRequestBody.java @@ -1,5 +1,10 @@ /** - * Copyright (c) 2011-2015 Bosch Software Innovations GmbH, Germany. All rights reserved. + * Copyright (c) 2015 Bosch Software Innovations 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 */ package org.eclipse.hawkbit.mgmt.json.model.targetfilter; From d5d9c387efbc2312cc7a7b8f62c711bdffb1a16a Mon Sep 17 00:00:00 2001 From: Dominik Herbst Date: Fri, 23 Sep 2016 13:46:24 +0200 Subject: [PATCH 08/28] Corrected copyright headers Signed-off-by: Dominik Herbst --- .../hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryMapper.java | 2 +- .../mgmt/rest/resource/MgmtTargetFilterQueryResource.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryMapper.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryMapper.java index 629a1bc4e..d6d32ad8a 100644 --- a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryMapper.java +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryMapper.java @@ -1,6 +1,6 @@ /** * Copyright (c) 2015 Bosch Software Innovations 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 diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryResource.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryResource.java index cee14cf2c..e051e4d27 100644 --- a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryResource.java +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryResource.java @@ -1,6 +1,6 @@ /** * Copyright (c) 2015 Bosch Software Innovations 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 From 391001a717b49a17e6a772fbb4eb461e7cd71dd4 Mon Sep 17 00:00:00 2001 From: Dominik Herbst Date: Fri, 23 Sep 2016 15:39:19 +0200 Subject: [PATCH 09/28] Added missing message properties that were only in lang specific files Signed-off-by: Dominik Herbst --- .../src/main/resources/messages.properties | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/hawkbit-ui/src/main/resources/messages.properties b/hawkbit-ui/src/main/resources/messages.properties index eef0c3140..c3295ca5e 100644 --- a/hawkbit-ui/src/main/resources/messages.properties +++ b/hawkbit-ui/src/main/resources/messages.properties @@ -26,6 +26,7 @@ button.no.auto.assignment = none button.auto.assignment.desc = Select auto assign distribution set bulk.targets.upload = Please upload csv file. bulkupload.ds.name = DS Name +button.discard=Discard # Headers prefix with - header header.target.table=Targets @@ -50,6 +51,14 @@ header.dist.twintable.selected=Selected header.dist.twintable.available=Available header.target.installed = Installed header.target.assigned = Assigned +header.type=Type +header.swmodules=SwModules +header.migrations.step=IsRequiredMigrationStep +header.action=Actions +header.action.run=Run +header.action.pause=Pause +header.action.update=Edit +header.status=Status # Captions prefix with - caption caption.action.history = Action history for {0} @@ -170,6 +179,7 @@ label.configuration.anonymous.download = Allow targets to download artifacts wit label.unsupported.browser.ie=Sorry! current browser is not supported. Please use Internet Explorer 11 and above label.auto.assign.description=When an auto assign distribution set is selected, it will be automatically assigned to all targets that match the target filter. label.auto.assign.enable=Enable auto assignment +label.scheduled=Scheduled # Checkbox label prefix with - checkbox checkbox.dist.migration.required = Required Migration Step : @@ -512,3 +522,9 @@ menu.title = Software Provisioning #Target Filter Management breadcrumb.target.filter.custom.filters = Custom Filters + +notification.configuration.save=Saved changes +controller.polling.title=Polling Configuration +controller.polling.time=Polling Time +controller.polling.overduetime=Polling Overdue Time + From 5e7a607b9bc51228ab042e157890cdecd7e9c86d Mon Sep 17 00:00:00 2001 From: Dominik Herbst Date: Tue, 27 Sep 2016 09:41:12 +0200 Subject: [PATCH 10/28] Reverted whitespaces in messages.properties file Signed-off-by: Dominik Herbst --- .editorconfig | 11 ++++ .../src/main/resources/messages.properties | 66 +++++++++---------- 2 files changed, 44 insertions(+), 33 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..6695d3473 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,11 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf + +[*.properties] +trim_trailing_whitespace = false diff --git a/hawkbit-ui/src/main/resources/messages.properties b/hawkbit-ui/src/main/resources/messages.properties index c3295ca5e..677064b0f 100644 --- a/hawkbit-ui/src/main/resources/messages.properties +++ b/hawkbit-ui/src/main/resources/messages.properties @@ -108,23 +108,23 @@ caption.confirm.assign.consequences = Auto assign consequences caption.auto.assignment.ds = Auto assignment # Labels prefix with - label -label.dist.details.type = Type : -label.dist.details.name = Name : -label.dist.details.version = Version : -label.dist.details.vendor = Vendor : -label.dist.details.jvm = Runtime : -label.dist.details.ah = Application : -label.dist.details.os = OS : -label.modified.date = Last modified at : -label.modified.by = Last modified by : -label.created.at = Created at : -label.created.by = Created by : -label.target.count = Targets : -label.description = Description : -label.ip = Address : -label.type = Type : -label.assigned.type = Assignment type : -label.assigned.count = {0} Assigned +label.dist.details.type = Type : +label.dist.details.name = Name : +label.dist.details.version = Version : +label.dist.details.vendor = Vendor : +label.dist.details.jvm = Runtime : +label.dist.details.ah = Application : +label.dist.details.os = OS : +label.modified.date = Last modified at : +label.modified.by = Last modified by : +label.created.at = Created at : +label.created.by = Created by : +label.target.count = Targets : +label.description = Description : +label.ip = Address : +label.type = Type : +label.assigned.type = Assignment type : +label.assigned.count = {0} Assigned label.installed.count = {0} Installed label.mandatory.field = * Mandatory Field label.components.drop.area = Drop here to delete @@ -164,9 +164,9 @@ label.cancelling = Canceling label.retrieved = Retrieved label.download = Downloading label.unknown = Unknown -label.target.id = Controller Id : -label.target.ip = Controller IP : -label.target.security.token = Security token : +label.target.id = Controller Id : +label.target.ip = Controller IP : +label.target.security.token = Security token : label.filter.by.status = Filter by Status label.target.controller.attrs = Controller attributes label.target.lastpolldate = Last poll : @@ -182,7 +182,7 @@ label.auto.assign.enable=Enable auto assignment label.scheduled=Scheduled # Checkbox label prefix with - checkbox -checkbox.dist.migration.required = Required Migration Step : +checkbox.dist.migration.required = Required Migration Step : checkbox.dist.required.migration.step = Required Migration Step # TextFields prefix with - textfield @@ -198,7 +198,7 @@ prompt.target.id = Controller ID #Tooltips prefix with - tooltip -tooltip.add.module = Add Software Module +tooltip.add.module = Add Software Module tooltip.status.unknown = Unknown tooltip.status.registered = Registered tooltip.status.pending = Pending @@ -252,7 +252,7 @@ message.software.type.discard.success = All software moduleTypes selected for de message.assign.software.discard.success = All software moduleTypes selected for assign are discarded successfully ! message.software.delete.success = All software modules selected for delete are deleted successfully ! message.software.type.delete.success = All software modules types selected for delete are deleted successfully ! -message.dist.set.type.deleted.success = {0} DistributionSetType deleted successfully ! +message.dist.set.type.deleted.success = {0} DistributionSetType deleted successfully ! message.new.dist.save.success = {0} - {1} saved successfully message.dist.update.success = {0} - {1} updated successfully message.duplicate.dist = Distribution set [{0}] or version [{1}] must be unique, entered value already exists. @@ -260,7 +260,7 @@ message.error.view = No such view: {0} message.accessdenied.view = No access to view: {0} message.no.data = No data available message.target.assignment = {0} Assignment(s) done -message.target.deleted = {0} Target(s) deleted +message.target.deleted = {0} Target(s) deleted message.dist.deleted = {0} Distribution set(s) deleted message.tag.update.mandatory = Please select the Tag to update message.tag.duplicate.check = {0} already exists, please enter another value @@ -286,7 +286,7 @@ message.error.ah.softmodule = Please select the Application to delete message.error.softmodule.deleted = The selected Software Module is already deleted message.cancel.action = Cancel.. message.cancel.action.success = Action cancelled successfully ! -message.cancel.action.failed = Unable to cancel the action ! +message.cancel.action.failed = Unable to cancel the action ! message.cancel.action.confirm = Are you sure that you want to cancel this action? message.target.alreadyAssigned = {0} Target(s) were already assigned message.dist.alreadyAssigned = {0} Distribution Set(s) were already assigned @@ -295,8 +295,8 @@ message.force.action.confirm = Are you sure that you want to force this action? message.force.action.success = Action forced successfully ! message.forcequit.action = Force Quit.. message.forcequit.action.success = Action has been force quit successfully ! -message.forcequit.action.failed = Force Quitting the action is not possible ! -message.forcequit.action.confirm = Attention!\nForce quit should only be used when the assignment action is not working properly.\nForce quitting an action has no effect on the connected target. It is just resetting \nthe data stored on the SP update server. \nAre you absolutely sure that you want to force quit this action? +message.forcequit.action.failed = Force Quitting the action is not possible ! +message.forcequit.action.confirm = Attention!\nForce quit should only be used when the assignment action is not working properly.\nForce quitting an action has no effect on the connected target. It is just resetting \nthe data stored on the SP update server. \nAre you absolutely sure that you want to force quit this action? message.distribution.no.update = distribution {0} set is already assigned to targets and cannot be changed message.action.not.allowed = Action not allowed message.action.did.not.work = Action did not work. Please try again. @@ -318,14 +318,14 @@ message.update.filter.success = Custom filter updated Successfully! message.target.filter.validation = Please enter name and query message.target.filter.duplicate = {0} already exists, please enter another value message.tag.use.bulk.upload = {0} cannot be deleted .It is in use in targets bulk upload -message.bulk.upload.tag.assignment.failed = Tag {0} assignment failed as tag no longer exists -message.bulk.upload.tag.assignments.failed= Few tag assignments failed as tags no longer exists +message.bulk.upload.tag.assignment.failed = Tag {0} assignment failed as tag no longer exists +message.bulk.upload.tag.assignments.failed= Few tag assignments failed as tags no longer exists message.confirm.assign.consequences.none = This auto assignment will not have any effect on the currently available targets. In future added targets might match the filter and will receive the selected distribution set automatically. message.confirm.assign.consequences.text = When you confirm this auto assignment, {0} targets which match the filter will immediately get assigned with the selected distribution set. # action info action.target.table.selectall = Select all (Ctrl+A) -action.target.table.clear = Clear selections +action.target.table.clear = Clear selections #reused messages soft.module.jvm =Runtime @@ -336,8 +336,8 @@ soft.module.os =OS message.error.noFileSelected = No file selected for upload message.error.noProvidedName = Please provide custom file name message.error.noSwModuleSelected = Please select a Software Module -message.no.duplicateFiles = Duplicate files selected -message.no.duplicateFile = Duplicate file selected : +message.no.duplicateFiles = Duplicate files selected +message.no.duplicateFile = Duplicate file selected : message.delete.artifact = Are you sure that you want to delete artifact {0} ? message.duplicate.filename = Duplicate file name message.swModule.deleted = {0} Software Module(s) deleted @@ -351,7 +351,7 @@ message.abort.upload = Are you sure that you want to abort the upload? upload.swModuleTable.header = Software Module -upload.selectedfile.name = file selected for upload +upload.selectedfile.name = file selected for upload upload.file.name = File name upload.sha1 = SHA1 checksum upload.md5 = MD5 checksum From 12b0ffa614188c4c8e7c71eec278407451e09a32 Mon Sep 17 00:00:00 2001 From: Dominik Herbst Date: Wed, 28 Sep 2016 11:04:07 +0200 Subject: [PATCH 11/28] Removed .editorconfig Signed-off-by: Dominik Herbst --- .editorconfig | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 6695d3473..000000000 --- a/.editorconfig +++ /dev/null @@ -1,11 +0,0 @@ -# EditorConfig is awesome: http://EditorConfig.org - -# top-most EditorConfig file -root = true - -# Unix-style newlines with a newline ending every file -[*] -end_of_line = lf - -[*.properties] -trim_trailing_whitespace = false From 78e9705886f5d8f86beb1cf186a933649882439e Mon Sep 17 00:00:00 2001 From: Dominik Herbst Date: Wed, 28 Sep 2016 14:46:33 +0200 Subject: [PATCH 12/28] Corrected messages Signed-off-by: Dominik Herbst --- .../src/main/resources/messages.properties | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/hawkbit-ui/src/main/resources/messages.properties b/hawkbit-ui/src/main/resources/messages.properties index 677064b0f..4141cd278 100644 --- a/hawkbit-ui/src/main/resources/messages.properties +++ b/hawkbit-ui/src/main/resources/messages.properties @@ -141,11 +141,11 @@ label.combobox.type = Select Type label.combobox.tag = Select Tag label.choose.tag = Choose Tag to update label.choose.tag.color = Choose Tag Color -label.filter = Filter: -label.target.filter.count = Total Targets: label.target.filtered.total = Total filtered targets : -label.filter.selected = Selected: -label.filter.shown = Shown: +label.filter = Filter : +label.target.filter.count = Total Targets : +label.filter.selected = Selected : +label.filter.shown = Shown : label.filter.targets = Filtered targets : label.filter.status = Status, label.filter.tags = Tags, @@ -170,7 +170,6 @@ label.target.security.token = Security token : label.filter.by.status = Filter by Status label.target.controller.attrs = Controller attributes label.target.lastpolldate = Last poll : -label.no.tag.assigned = NO TAG label.tag.name = Tag name label.configuration.auth.header = Allow targets to authenticate via a certificate authenticated by an reverse proxy label.configuration.auth.gatewaytoken = Allow a gateway to authenticate and manage multiple targets through a gateway security token @@ -244,7 +243,6 @@ message.dists.already.deleted = Few distribution(s) are already deleted.Pending message.target.deleted.pending = Target(s) already deleted.Pending for action message.dist.deleted.pending = Distribution(s) already deleted.Pending for action message.dist.delete.success = All selected distribution sets are deleted successfully ! -message.dist.discard.success = All distributions selected for delete are discarded successfully ! message.target.delete.success = All selected targets are deleted successfully ! message.target.discard.success = All targets selected for delete are discarded successfully ! message.software.discard.success = All software modules selected for delete are discarded successfully ! @@ -309,7 +307,6 @@ message.no.targets.assiged.fortag = No targets are assigned to tag {0} message.error.missing.tagname = Please select tag name message.type.delete = Please unclick the distribution type {0}, then try to delete message.error.dist.set.type.update= Distribution Set Type is already assigned to targets and cannot be changed -message.target.ds.assign.success = Assignments saved successfully ! message.no.directory.upload = Directory upload is not supported message.delete.filter.confirm = Are you sure that you want to delete custom filter? message.delete.filter.success = Custom filter {0} deleted Successfully! @@ -380,6 +377,7 @@ custom.created.date = Created Date #Manage distributions view label.drop.dist.delete.area = Drop here
to delete +label.no.tag.assigned = NO TAG caption.assign.software.dist.accordion.tab = Assign Software Modules message.software.assignment = {0} Software Module(s) Assignment(s) done message.dist.inuse = {0} Distribution is already assigned to target @@ -389,7 +387,9 @@ message.target.assigned = {0} is assigned to {1} message.dist.type.delete = {0} DistributionType(s) Deleted successfully. message.sw.module.type.delete = {0} Software Module Type(s) deleted successfully. message.dist.type.discard.success = All Distribution Types are discarded successfully ! +message.dist.discard.success = All Distributions are discarded successfully ! message.assign.discard.success = All assignments are discarded successfully ! +message.target.ds.assign.success = Assignments saved successfully ! message.bulk.upload.assignment.failed = Distribution set assignment failed as distribution set no longer exists! message.key.missing = Key is missing ! message.value.missing = Value is missing ! @@ -460,7 +460,7 @@ header.assigned.ds = Assigned DS header.installed.ds = Installed DS header.target.status = Status header.target.tags = Tags -header.total.targets = Total targets +header.total.targets = Total targets header.key = Key header.value = Value header.auto.assignment.ds = Auto assignment From 38bd54fe6ec62e598040bf77277651314210ee1d Mon Sep 17 00:00:00 2001 From: Dominik Herbst Date: Fri, 30 Sep 2016 14:26:20 +0200 Subject: [PATCH 13/28] Code quality impovements, additional tests to find target filter queries Signed-off-by: Dominik Herbst --- .../targetfilter/MgmtTargetFilterQuery.java | 27 +------- .../MgmtTargetFilterQueryRequestBody.java | 3 +- .../resource/MgmtDistributionSetResource.java | 6 +- .../resource/MgmtTargetFilterQueryMapper.java | 27 ++++---- .../MgmtTargetFilterQueryResource.java | 25 +++----- .../MgmtDistributionSetResourceTest.java | 63 ++++++++++++++++++- ...a => TargetFilterQueryManagementTest.java} | 12 +++- .../jpa/autoassign/AutoAssignCheckerTest.java | 5 +- .../TargetFilterQueryDetailsTable.java | 2 +- .../dstable/ManageDistBeanQuery.java | 8 +-- 10 files changed, 105 insertions(+), 73 deletions(-) rename hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/{TargetFilterQueryManagenmentTest.java => TargetFilterQueryManagementTest.java} (95%) diff --git a/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/targetfilter/MgmtTargetFilterQuery.java b/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/targetfilter/MgmtTargetFilterQuery.java index 99ba58897..2a3acbe53 100644 --- a/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/targetfilter/MgmtTargetFilterQuery.java +++ b/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/targetfilter/MgmtTargetFilterQuery.java @@ -10,14 +10,14 @@ package org.eclipse.hawkbit.mgmt.json.model.targetfilter; import org.eclipse.hawkbit.mgmt.json.model.MgmtBaseEntity; -import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonProperty; /** - * A json annotated rest model for Target to RESTful API representation. + * A json annotated rest model for Target Filter Queries to RESTful API + * representation. * */ @JsonInclude(Include.ALWAYS) @@ -36,48 +36,26 @@ public class MgmtTargetFilterQuery extends MgmtBaseEntity { @JsonProperty private Long autoAssignDistributionSet; - /** - * @return the filterId - */ public Long getFilterId() { return filterId; } - /** - * @param filterId - * the filterId to set - */ public void setFilterId(final Long filterId) { this.filterId = filterId; } - /** - * @return the name - */ public String getName() { return name; } - /** - * @param name - * the name to set - */ public void setName(final String name) { this.name = name; } - /** - * @return the query - */ public String getQuery() { return query; } - /** - * @param query - * the query to set - */ - @JsonIgnore public void setQuery(final String query) { this.query = query; } @@ -86,7 +64,6 @@ public class MgmtTargetFilterQuery extends MgmtBaseEntity { return autoAssignDistributionSet; } - @JsonIgnore public void setAutoAssignDistributionSet(final Long autoAssignDistributionSet) { this.autoAssignDistributionSet = autoAssignDistributionSet; } diff --git a/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/targetfilter/MgmtTargetFilterQueryRequestBody.java b/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/targetfilter/MgmtTargetFilterQueryRequestBody.java index 8cbc87d2e..746b3eb08 100644 --- a/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/targetfilter/MgmtTargetFilterQueryRequestBody.java +++ b/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/targetfilter/MgmtTargetFilterQueryRequestBody.java @@ -32,9 +32,8 @@ public class MgmtTargetFilterQueryRequestBody { * @param name * the name to set */ - public MgmtTargetFilterQueryRequestBody setName(final String name) { + public void setName(final String name) { this.name = name; - return this; } /** diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResource.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResource.java index 1da8d8a5f..055ed7399 100644 --- a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResource.java +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResource.java @@ -236,8 +236,6 @@ public class MgmtDistributionSetResource implements MgmtDistributionSetRestApi { @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) int pagingLimitParam, @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) String sortParam, @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) String rsqlParam) { - // check if distribution set exists otherwise throw exception - // immediately DistributionSet distributionSet = findDistributionSetWithExceptionIfNotFound(distributionSetId); final int sanitizedOffsetParam = PagingUtility.sanitizeOffsetParam(pagingOffsetParam); @@ -247,10 +245,10 @@ public class MgmtDistributionSetResource implements MgmtDistributionSetRestApi { final Pageable pageable = new OffsetBasedPageRequest(sanitizedOffsetParam, sanitizedLimitParam, sorting); final Page targetFilterQueries; if (rsqlParam != null) { - targetFilterQueries = this.targetFilterQueryManagement.findTargetFilterQueryByAutoAssignDS(pageable, + targetFilterQueries = targetFilterQueryManagement.findTargetFilterQueryByAutoAssignDS(pageable, distributionSet, rsqlParam); } else { - targetFilterQueries = this.targetFilterQueryManagement.findTargetFilterQueryByAutoAssignDS(pageable, + targetFilterQueries = targetFilterQueryManagement.findTargetFilterQueryByAutoAssignDS(pageable, distributionSet); } diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryMapper.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryMapper.java index d6d32ad8a..b2a23e259 100644 --- a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryMapper.java +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryMapper.java @@ -11,8 +11,9 @@ package org.eclipse.hawkbit.mgmt.rest.resource; import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; -import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQuery; import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQueryRequestBody; @@ -20,6 +21,7 @@ import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetFilterQueryRestApi; import org.eclipse.hawkbit.repository.EntityFactory; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.TargetFilterQuery; +import org.springframework.util.CollectionUtils; /** * A mapper which maps repository model to RESTful model representation and @@ -33,34 +35,27 @@ public final class MgmtTargetFilterQueryMapper { } /** - * Create a response for targets. + * Create a response for target filter queries. * * @param filters * list of targets * @return the response */ - public static List toResponse(final Iterable filters) { - final List mappedList = new ArrayList<>(); - if (filters != null) { - for (final TargetFilterQuery filter : filters) { - final MgmtTargetFilterQuery response = toResponse(filter); - mappedList.add(response); - } + public static List toResponse(final List filters) { + if (CollectionUtils.isEmpty(filters)) { + return Collections.emptyList(); } - return mappedList; + return filters.stream().map(MgmtTargetFilterQueryMapper::toResponse).collect(Collectors.toList()); } /** - * Create a response for target. + * Create a response for target filter query. * * @param filter * the target * @return the response */ public static MgmtTargetFilterQuery toResponse(final TargetFilterQuery filter) { - if (filter == null) { - return null; - } final MgmtTargetFilterQuery targetRest = new MgmtTargetFilterQuery(); targetRest.setFilterId(filter.getId()); targetRest.setName(filter.getName()); @@ -78,7 +73,9 @@ public final class MgmtTargetFilterQueryMapper { } targetRest.add(linkTo(methodOn(MgmtTargetFilterQueryRestApi.class).getFilter(filter.getId())).withRel("self")); - targetRest.add(linkTo(methodOn(MgmtTargetFilterQueryRestApi.class).postAssignedDistributionSet(filter.getId(),null)).withRel("autoAssignDS")); + targetRest.add( + linkTo(methodOn(MgmtTargetFilterQueryRestApi.class).postAssignedDistributionSet(filter.getId(), null)) + .withRel("autoAssignDS")); return targetRest; } diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryResource.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryResource.java index e051e4d27..5fcfdcf93 100644 --- a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryResource.java +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryResource.java @@ -78,13 +78,13 @@ public class MgmtTargetFilterQueryResource implements MgmtTargetFilterQueryRestA final Slice findTargetFiltersAll; final Long countTargetsAll; if (rsqlParam != null) { - final Page findFilterPage = this.filterManagement + final Page findFilterPage = filterManagement .findTargetFilterQueryByFilter(pageable, rsqlParam); countTargetsAll = findFilterPage.getTotalElements(); findTargetFiltersAll = findFilterPage; } else { - findTargetFiltersAll = this.filterManagement.findAllTargetFilterQuery(pageable); - countTargetsAll = this.filterManagement.countAllTargetFilterQuery(); + findTargetFiltersAll = filterManagement.findAllTargetFilterQuery(pageable); + countTargetsAll = filterManagement.countAllTargetFilterQuery(); } final List rest = MgmtTargetFilterQueryMapper @@ -94,7 +94,7 @@ public class MgmtTargetFilterQueryResource implements MgmtTargetFilterQueryRestA @Override public ResponseEntity createFilter(@RequestBody MgmtTargetFilterQueryRequestBody filter) { - final TargetFilterQuery createdTarget = this.filterManagement + final TargetFilterQuery createdTarget = filterManagement .createTargetFilterQuery(MgmtTargetFilterQueryMapper.fromRequest(entityFactory, filter)); return new ResponseEntity<>(MgmtTargetFilterQueryMapper.toResponse(createdTarget), HttpStatus.CREATED); @@ -113,7 +113,7 @@ public class MgmtTargetFilterQueryResource implements MgmtTargetFilterQueryRestA existingFilter.setQuery(targetFilterRest.getQuery()); } - final TargetFilterQuery updateFilter = this.filterManagement.updateTargetFilterQuery(existingFilter); + final TargetFilterQuery updateFilter = filterManagement.updateTargetFilterQuery(existingFilter); return new ResponseEntity<>(MgmtTargetFilterQueryMapper.toResponse(updateFilter), HttpStatus.OK); } @@ -121,7 +121,7 @@ public class MgmtTargetFilterQueryResource implements MgmtTargetFilterQueryRestA @Override public ResponseEntity deleteFilter(@PathVariable("filterId") Long filterId) { final TargetFilterQuery filter = findFilterWithExceptionIfNotFound(filterId); - this.filterManagement.deleteTargetFilterQuery(filter.getId()); + filterManagement.deleteTargetFilterQuery(filter.getId()); LOG.debug("{} target filter query deleted, return status {}", filterId, HttpStatus.OK); return new ResponseEntity<>(HttpStatus.OK); } @@ -139,7 +139,7 @@ public class MgmtTargetFilterQueryResource implements MgmtTargetFilterQueryRestA filter.setAutoAssignDistributionSet(distributionSet); - final TargetFilterQuery updateFilter = this.filterManagement.updateTargetFilterQuery(filter); + final TargetFilterQuery updateFilter = filterManagement.updateTargetFilterQuery(filter); return new ResponseEntity<>(MgmtTargetFilterQueryMapper.toResponse(updateFilter), HttpStatus.OK); } @@ -149,12 +149,7 @@ public class MgmtTargetFilterQueryResource implements MgmtTargetFilterQueryRestA final TargetFilterQuery filter = findFilterWithExceptionIfNotFound(filterId); DistributionSet autoAssignDistributionSet = filter.getAutoAssignDistributionSet(); MgmtDistributionSet distributionSetRest = MgmtDistributionSetMapper.toResponse(autoAssignDistributionSet); - final HttpStatus retStatus; - if (distributionSetRest == null) { - retStatus = HttpStatus.NO_CONTENT; - } else { - retStatus = HttpStatus.OK; - } + final HttpStatus retStatus = distributionSetRest == null ? HttpStatus.NO_CONTENT : HttpStatus.OK; return new ResponseEntity<>(distributionSetRest, retStatus); } @@ -164,13 +159,13 @@ public class MgmtTargetFilterQueryResource implements MgmtTargetFilterQueryRestA filter.setAutoAssignDistributionSet(null); - this.filterManagement.updateTargetFilterQuery(filter); + filterManagement.updateTargetFilterQuery(filter); return new ResponseEntity<>(HttpStatus.NO_CONTENT); } private TargetFilterQuery findFilterWithExceptionIfNotFound(final Long filterId) { - final TargetFilterQuery filter = this.filterManagement.findTargetFilterQueryById(filterId); + final TargetFilterQuery filter = filterManagement.findTargetFilterQueryById(filterId); if (filter == null) { throw new EntityNotFoundException("TargetFilterQuery with Id {" + filterId + "} does not exist"); } diff --git a/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResourceTest.java b/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResourceTest.java index d59c114e5..62b8548f1 100644 --- a/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResourceTest.java +++ b/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResourceTest.java @@ -302,9 +302,9 @@ public class MgmtDistributionSetResourceTest extends AbstractRestIntegrationTest final Set createDistributionSetsAlphabetical = createDistributionSetsAlphabetical(1); final DistributionSet createdDs = createDistributionSetsAlphabetical.iterator().next(); - final TargetFilterQuery tfq = targetFilterQueryManagement + targetFilterQueryManagement .createTargetFilterQuery(entityFactory.generateTargetFilterQuery(knownFilterName, "x==y", createdDs)); - // create some dummy targets which are not assigned or installed + // create some dummy target filter queries targetFilterQueryManagement.createTargetFilterQuery(entityFactory.generateTargetFilterQuery("b", "x==y")); targetFilterQueryManagement.createTargetFilterQuery(entityFactory.generateTargetFilterQuery("c", "x==y")); @@ -313,6 +313,65 @@ public class MgmtDistributionSetResourceTest extends AbstractRestIntegrationTest .andExpect(jsonPath("$.content[0].name", equalTo(knownFilterName))); } + @Test + @Description("Ensures that an error is returned when the query is invalid.") + public void getAutoAssignTargetFiltersOfDSWithInvalidFilter() throws Exception { + // prepare distribution set + final Set createDistributionSetsAlphabetical = createDistributionSetsAlphabetical(1); + final DistributionSet createdDs = createDistributionSetsAlphabetical.iterator().next(); + final String invalidQuery = "unknownField=le=42"; + + mvc.perform(get(MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING + "/" + createdDs.getId() + + "/autoAssignTargetFilters").param(MgmtRestConstants.REQUEST_PARAMETER_SEARCH, invalidQuery)) + .andExpect(status().isBadRequest()); + } + + @Test + @Description("Ensures that target filters with auto assign DS are returned according to the query.") + public void getMultipleAutoAssignTargetFiltersOfDistributionSet() throws Exception { + final String filterNamePrefix = "filter-"; + final Set createDistributionSetsAlphabetical = createDistributionSetsAlphabetical(1); + final DistributionSet createdDs = createDistributionSetsAlphabetical.iterator().next(); + final String query = "name==" + filterNamePrefix + "*"; + + // create target filter queries that should be found + targetFilterQueryManagement.createTargetFilterQuery( + entityFactory.generateTargetFilterQuery(filterNamePrefix + "1", "x==y", createdDs)); + targetFilterQueryManagement.createTargetFilterQuery( + entityFactory.generateTargetFilterQuery(filterNamePrefix + "2", "x==z", createdDs)); + // create some dummy target filter queries + targetFilterQueryManagement.createTargetFilterQuery(entityFactory.generateTargetFilterQuery("b", "x==y")); + targetFilterQueryManagement.createTargetFilterQuery(entityFactory.generateTargetFilterQuery("c", "x==y")); + + mvc.perform(get(MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING + "/" + createdDs.getId() + + "/autoAssignTargetFilters").param(MgmtRestConstants.REQUEST_PARAMETER_SEARCH, query)) + .andExpect(status().isOk()).andExpect(jsonPath("$.size", equalTo(2))) + .andExpect(jsonPath("$.content[0].name", equalTo(filterNamePrefix + "1"))) + .andExpect(jsonPath("$.content[1].name", equalTo(filterNamePrefix + "2"))); + } + + @Test + @Description("Ensures that no target filters are returned according to the non matching query.") + public void getEmptyAutoAssignTargetFiltersOfDistributionSet() throws Exception { + final String filterNamePrefix = "filter-"; + final Set createDistributionSetsAlphabetical = createDistributionSetsAlphabetical(1); + final DistributionSet createdDs = createDistributionSetsAlphabetical.iterator().next(); + final String query = "name==doesNotExist"; + + // create target filter queries that should be found + targetFilterQueryManagement.createTargetFilterQuery( + entityFactory.generateTargetFilterQuery(filterNamePrefix + "1", "x==y", createdDs)); + targetFilterQueryManagement.createTargetFilterQuery( + entityFactory.generateTargetFilterQuery(filterNamePrefix + "2", "x==z", createdDs)); + // create some dummy target filter queries + targetFilterQueryManagement.createTargetFilterQuery(entityFactory.generateTargetFilterQuery("b", "x==y")); + targetFilterQueryManagement.createTargetFilterQuery(entityFactory.generateTargetFilterQuery("c", "x==y")); + + mvc.perform(get(MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING + "/" + createdDs.getId() + + "/autoAssignTargetFilters").param(MgmtRestConstants.REQUEST_PARAMETER_SEARCH, query)) + .andExpect(status().isOk()).andExpect(jsonPath("$.size", equalTo(0))); + } + @Test @Description("Ensures that DS in repository are listed with proper paging properties.") public void getDistributionSetsWithoutAddtionalRequestParameters() throws Exception { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetFilterQueryManagenmentTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetFilterQueryManagementTest.java similarity index 95% rename from hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetFilterQueryManagenmentTest.java rename to hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetFilterQueryManagementTest.java index b51511d17..efde4e493 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetFilterQueryManagenmentTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetFilterQueryManagementTest.java @@ -10,6 +10,7 @@ package org.eclipse.hawkbit.repository.jpa; import org.eclipse.hawkbit.repository.TargetFilterQueryManagement; import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException; +import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet; import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery; import org.eclipse.hawkbit.repository.model.DistributionSet; @@ -35,7 +36,7 @@ import static org.junit.Assert.*; */ @Features("Component Tests - Repository") @Stories("Target Filter Query Management") -public class TargetFilterQueryManagenmentTest extends AbstractJpaIntegrationTest { +public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Test creation of target filter query.") @@ -63,6 +64,15 @@ public class TargetFilterQueryManagenmentTest extends AbstractJpaIntegrationTest assertEquals("Retrieved newly created custom target filter", targetFilterQuery, results.get(0)); } + @Test(expected = RSQLParameterUnsupportedFieldException.class) + @Description("Test searching a target filter query with an invalid filter.") + public void searchTargetFilterQueryInvalidField() { + // Should throw an exception + targetFilterQueryManagement.findTargetFilterQueryByFilter(new PageRequest(0, 10), "unknownField==testValue") + .getContent(); + + } + @Test @Description("Checks if the EntityAlreadyExistsException is thrown if a targetfilterquery with the same name are created more than once.") public void createDuplicateTargetFilterQuery() { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignCheckerTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignCheckerTest.java index dd7293fb2..5ce015550 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignCheckerTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignCheckerTest.java @@ -10,19 +10,16 @@ package org.eclipse.hawkbit.repository.jpa.autoassign; import java.util.List; import java.util.stream.Collectors; -import java.util.stream.Stream; import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet; import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery; import org.eclipse.hawkbit.repository.model.DistributionSet; -import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.TargetFilterQuery; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Slice; import ru.yandex.qatools.allure.annotations.Description; @@ -154,4 +151,4 @@ public class AutoAssignCheckerTest extends AbstractJpaIntegrationTest { } -} \ No newline at end of file +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/detailslayout/TargetFilterQueryDetailsTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/detailslayout/TargetFilterQueryDetailsTable.java index 3371e0d64..98c91eb54 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/detailslayout/TargetFilterQueryDetailsTable.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/detailslayout/TargetFilterQueryDetailsTable.java @@ -56,7 +56,7 @@ public class TargetFilterQueryDetailsTable extends Table { */ public void populateTableByDistributionSet(final DistributionSet distributionSet) { removeAllItems(); - if (null == distributionSet) { + if (distributionSet == null) { return; } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/ManageDistBeanQuery.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/ManageDistBeanQuery.java index c6443d042..13c6a0c85 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/ManageDistBeanQuery.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/ManageDistBeanQuery.java @@ -41,12 +41,12 @@ public class ManageDistBeanQuery extends AbstractBeanQuery { private static final long serialVersionUID = 5176481314404662215L; private Sort sort = new Sort(Direction.ASC, "createdAt"); - private String searchText = null; + private String searchText; private transient DistributionSetManagement distributionSetManagement; - private transient Page firstPageDistributionSets = null; + private transient Page firstPageDistributionSets; - private DistributionSetType distributionSetType = null; - private Boolean dsComplete = null; + private DistributionSetType distributionSetType; + private Boolean dsComplete; /** * From f481e097db2aca1aac9e7bba9ac977387351008f Mon Sep 17 00:00:00 2001 From: Dominik Herbst Date: Tue, 4 Oct 2016 08:19:44 +0200 Subject: [PATCH 14/28] Using AutoAssignProperties with EnableConfigurationProperties. Code quality improvements. Signed-off-by: Dominik Herbst --- .../MgmtTargetFilterQueryRequestBody.java | 14 -------------- .../hawkbit/repository/AutoAssignProperties.java | 2 -- .../jpa/autoassign/AutoAssignScheduler.java | 2 ++ .../distributions/dstable/ManageDistBeanQuery.java | 6 +++--- 4 files changed, 5 insertions(+), 19 deletions(-) diff --git a/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/targetfilter/MgmtTargetFilterQueryRequestBody.java b/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/targetfilter/MgmtTargetFilterQueryRequestBody.java index 746b3eb08..ecfe1291b 100644 --- a/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/targetfilter/MgmtTargetFilterQueryRequestBody.java +++ b/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/targetfilter/MgmtTargetFilterQueryRequestBody.java @@ -21,32 +21,18 @@ public class MgmtTargetFilterQueryRequestBody { @JsonProperty(required = true) private String query; - /** - * @return the name - */ public String getName() { return name; } - /** - * @param name - * the name to set - */ public void setName(final String name) { this.name = name; } - /** - * @return the filter query - */ public String getQuery() { return query; } - /** - * @param query - * the filter query - */ public void setQuery(String query) { this.query = query; } diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/AutoAssignProperties.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/AutoAssignProperties.java index 9f6c85c00..2c9bd7849 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/AutoAssignProperties.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/AutoAssignProperties.java @@ -9,13 +9,11 @@ package org.eclipse.hawkbit.repository; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.stereotype.Component; /** * Rollout Management properties. * */ -@Component @ConfigurationProperties("hawkbit.autoassign") public class AutoAssignProperties { /** diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignScheduler.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignScheduler.java index 81ec130dc..87c5a48a7 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignScheduler.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignScheduler.java @@ -17,6 +17,7 @@ import org.eclipse.hawkbit.tenancy.TenantAware; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Profile; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @@ -27,6 +28,7 @@ import org.springframework.stereotype.Component; @Component // don't active the auto assign scheduler in test, otherwise it is hard to test @Profile("!test") +@EnableConfigurationProperties(AutoAssignProperties.class) public class AutoAssignScheduler { private static final Logger LOGGER = LoggerFactory.getLogger(AutoAssignScheduler.class); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/ManageDistBeanQuery.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/ManageDistBeanQuery.java index 13c6a0c85..81ece7340 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/ManageDistBeanQuery.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/ManageDistBeanQuery.java @@ -64,17 +64,17 @@ public class ManageDistBeanQuery extends AbstractBeanQuery { if (!Strings.isNullOrEmpty(searchText)) { searchText = String.format("%%%s%%", searchText); } - if (null != queryConfig.get(SPUIDefinitions.FILTER_BY_DISTRIBUTION_SET_TYPE)) { + if (queryConfig.get(SPUIDefinitions.FILTER_BY_DISTRIBUTION_SET_TYPE) != null) { distributionSetType = (DistributionSetType) queryConfig .get(SPUIDefinitions.FILTER_BY_DISTRIBUTION_SET_TYPE); } - if(null != queryConfig.get(SPUIDefinitions.FILTER_BY_DS_COMPLETE)) { + if(queryConfig.get(SPUIDefinitions.FILTER_BY_DS_COMPLETE) != null) { dsComplete = (Boolean)queryConfig.get(SPUIDefinitions.FILTER_BY_DS_COMPLETE); } } if (sortStates.length > 0) { - // Initalize sort + // Initialize sort sort = new Sort(sortStates[0] ? Direction.ASC : Direction.DESC, (String) sortPropertyIds[0]); // Add sort for (int distId = 1; distId < sortPropertyIds.length; distId++) { From 339f45ca6a79245f7b51dcd9a6c9cf7e2d12a4ad Mon Sep 17 00:00:00 2001 From: Dominik Herbst Date: Tue, 4 Oct 2016 11:57:56 +0200 Subject: [PATCH 15/28] Fixed sanitizing of filter query sort parameters. Corrected name of TargetManagement method Signed-off-by: Dominik Herbst --- .../resource/MgmtDistributionSetResource.java | 2 +- .../MgmtTargetFilterQueryResource.java | 2 +- .../mgmt/rest/resource/PagingUtility.java | 9 +++++++++ .../hawkbit/repository/TargetManagement.java | 20 ++++++++++--------- .../repository/jpa/JpaTargetManagement.java | 6 +++--- .../jpa/autoassign/AutoAssignChecker.java | 2 +- .../jpa/TargetManagementSearchTest.java | 3 +-- .../jpa/autoassign/AutoAssignCheckerTest.java | 2 +- .../DistributionSetSelectWindow.java | 2 +- 9 files changed, 29 insertions(+), 19 deletions(-) diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResource.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResource.java index 055ed7399..03ec68648 100644 --- a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResource.java +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResource.java @@ -240,7 +240,7 @@ public class MgmtDistributionSetResource implements MgmtDistributionSetRestApi { final int sanitizedOffsetParam = PagingUtility.sanitizeOffsetParam(pagingOffsetParam); final int sanitizedLimitParam = PagingUtility.sanitizePageLimitParam(pagingLimitParam); - final Sort sorting = PagingUtility.sanitizeTargetSortParam(sortParam); + final Sort sorting = PagingUtility.sanitizeTargetFilterQuerySortParam(sortParam); final Pageable pageable = new OffsetBasedPageRequest(sanitizedOffsetParam, sanitizedLimitParam, sorting); final Page targetFilterQueries; diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryResource.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryResource.java index 5fcfdcf93..1fc7c6c85 100644 --- a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryResource.java +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryResource.java @@ -72,7 +72,7 @@ public class MgmtTargetFilterQueryResource implements MgmtTargetFilterQueryRestA final int sanitizedOffsetParam = PagingUtility.sanitizeOffsetParam(pagingOffsetParam); final int sanitizedLimitParam = PagingUtility.sanitizePageLimitParam(pagingLimitParam); - final Sort sorting = PagingUtility.sanitizeTargetSortParam(sortParam); + final Sort sorting = PagingUtility.sanitizeTargetFilterQuerySortParam(sortParam); final Pageable pageable = new OffsetBasedPageRequest(sanitizedOffsetParam, sanitizedLimitParam, sorting); final Slice findTargetFiltersAll; diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/PagingUtility.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/PagingUtility.java index abd9e9ec4..d70109e3d 100644 --- a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/PagingUtility.java +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/PagingUtility.java @@ -20,6 +20,7 @@ import org.eclipse.hawkbit.repository.SoftwareModuleFields; import org.eclipse.hawkbit.repository.SoftwareModuleMetadataFields; import org.eclipse.hawkbit.repository.SoftwareModuleTypeFields; import org.eclipse.hawkbit.repository.TargetFields; +import org.eclipse.hawkbit.repository.TargetFilterQueryFields; import org.eclipse.hawkbit.rest.util.SortUtility; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Direction; @@ -59,6 +60,14 @@ public final class PagingUtility { return new Sort(SortUtility.parse(TargetFields.class, sortParam)); } + static Sort sanitizeTargetFilterQuerySortParam(final String sortParam) { + if (sortParam == null) { + // default + return new Sort(Direction.ASC, TargetFilterQueryFields.NAME.getFieldName()); + } + return new Sort(SortUtility.parse(TargetFilterQueryFields.class, sortParam)); + } + static Sort sanitizeSoftwareModuleSortParam(final String sortParam) { if (sortParam == null) { // default 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 d8d9e3b20..d7c791db7 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 @@ -244,32 +244,34 @@ public interface TargetManagement { /** * Finds all targets for all the given parameter {@link TargetFilterQuery} - * and that don't have the specified assigned distribution set. - * and returns not the full target but {@link TargetIdName}. + * and that don't have the specified distribution set in their action + * history. * * @param pageRequest * the pageRequest to enhance the query for paging and sorting + * @param distributionSetId + * id of the {@link DistributionSet} * @param targetFilterQuery * {@link TargetFilterQuery} * @return the found {@link TargetIdName}s */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) - Page findAllTargetIdsByTargetFilterQueryAndNonDS(@NotNull Pageable pageRequest, - Long distributionSetId, - @NotNull TargetFilterQuery targetFilterQuery); + Page findAllTargetsByTargetFilterQueryAndNonDS(@NotNull Pageable pageRequest, Long distributionSetId, + @NotNull TargetFilterQuery targetFilterQuery); /** * Counts all targets for all the given parameter {@link TargetFilterQuery} - * and that don't have the specified assigned distribution set. and returns - * not the full target but {@link TargetIdName}. + * and that don't have the specified distribution set in their action + * history. * + * @param distributionSetId + * id of the {@link DistributionSet} * @param targetFilterQuery * {@link TargetFilterQuery} * @return the found {@link TargetIdName}s */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) - Long countTargetByTargetFilterQueryAndNonDS(Long distributionSetId, - @NotNull TargetFilterQuery targetFilterQuery); + Long countTargetsByTargetFilterQueryAndNonDS(Long distributionSetId, @NotNull TargetFilterQuery targetFilterQuery); /** * retrieves {@link Target}s by the assigned {@link DistributionSet} without 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 530d48d58..329fb3dfd 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 @@ -563,8 +563,8 @@ public class JpaTargetManagement implements TargetManagement { } @Override - public Page findAllTargetIdsByTargetFilterQueryAndNonDS(@NotNull Pageable pageRequest, - Long distributionSetId, @NotNull TargetFilterQuery targetFilterQuery) { + public Page findAllTargetsByTargetFilterQueryAndNonDS(@NotNull Pageable pageRequest, + Long distributionSetId, @NotNull TargetFilterQuery targetFilterQuery) { final Specification spec = RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class); @@ -577,7 +577,7 @@ public class JpaTargetManagement implements TargetManagement { } @Override - public Long countTargetByTargetFilterQueryAndNonDS(Long distributionSetId, @NotNull TargetFilterQuery targetFilterQuery) { + public Long countTargetsByTargetFilterQueryAndNonDS(Long distributionSetId, @NotNull TargetFilterQuery targetFilterQuery) { final Specification spec = RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class); final List> specList = new ArrayList<>(); specList.add(spec); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignChecker.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignChecker.java index f35aab453..ed001f481 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignChecker.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignChecker.java @@ -156,7 +156,7 @@ public class AutoAssignChecker { * @return list of targets with action type */ private List getTargetsWithActionType(TargetFilterQuery targetFilterQuery, Long dsId, int count) { - Page targets = targetManagement.findAllTargetIdsByTargetFilterQueryAndNonDS(new PageRequest(0, count), + Page targets = targetManagement.findAllTargetsByTargetFilterQueryAndNonDS(new PageRequest(0, count), dsId, targetFilterQuery); return targets.getContent().stream() diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementSearchTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementSearchTest.java index 58856f16c..e94eb355f 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementSearchTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementSearchTest.java @@ -25,7 +25,6 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag; import org.eclipse.hawkbit.repository.model.*; import org.eclipse.hawkbit.repository.model.Action.Status; import org.junit.Test; -import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Slice; import com.google.common.collect.Lists; @@ -733,7 +732,7 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest { deploymentManagement.assignDistributionSet(assignedSet, assignedTargets); - List result = targetManagement.findAllTargetIdsByTargetFilterQueryAndNonDS(pageReq, + List result = targetManagement.findAllTargetsByTargetFilterQueryAndNonDS(pageReq, assignedSet.getId(), tfq).getContent(); assertThat(result) .as("count of targets").hasSize(unassignedTargets.size()) diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignCheckerTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignCheckerTest.java index 5ce015550..47fab431e 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignCheckerTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignCheckerTest.java @@ -73,7 +73,7 @@ public class AutoAssignCheckerTest extends AbstractJpaIntegrationTest { verifyThatTargetsHaveDistributionSetAssignment(setB, targets.subList(10, 20), targetsCount); // Count the number of targets that will be assigned with setA - assertThat(targetManagement.countTargetByTargetFilterQueryAndNonDS(setA.getId(), targetFilterQuery)) + assertThat(targetManagement.countTargetsByTargetFilterQueryAndNonDS(setA.getId(), targetFilterQuery)) .isEqualTo(90); // Run the check diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/DistributionSetSelectWindow.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/DistributionSetSelectWindow.java index d1e77e529..f0f484526 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/DistributionSetSelectWindow.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/DistributionSetSelectWindow.java @@ -248,7 +248,7 @@ public class DistributionSetSelectWindow layout.setMargin(true); setContent(layout); - Long targetsCount = targetManagement.countTargetByTargetFilterQueryAndNonDS(distributionSetId, targetFilterQuery); + Long targetsCount = targetManagement.countTargetsByTargetFilterQueryAndNonDS(distributionSetId, targetFilterQuery); Label mainTextLabel; if(targetsCount == 0) { mainTextLabel = new Label(i18n.get("message.confirm.assign.consequences.none")); From 1063c9172427dca7b97cc4e455d15fbae868c762 Mon Sep 17 00:00:00 2001 From: Dominik Herbst Date: Tue, 4 Oct 2016 14:27:41 +0200 Subject: [PATCH 16/28] Creating AutoAssignScheduler and Checker in the Configuration class Signed-off-by: Dominik Herbst --- .../RepositoryApplicationConfiguration.java | 44 +++++++++ .../jpa/autoassign/AutoAssignChecker.java | 97 +++++++++++-------- .../jpa/autoassign/AutoAssignScheduler.java | 35 ++++--- 3 files changed, 122 insertions(+), 54 deletions(-) diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/RepositoryApplicationConfiguration.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/RepositoryApplicationConfiguration.java index a62de9a06..df6b8dcb3 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/RepositoryApplicationConfiguration.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/RepositoryApplicationConfiguration.java @@ -43,6 +43,8 @@ import org.eclipse.hawkbit.repository.jpa.JpaTargetManagement; import org.eclipse.hawkbit.repository.jpa.JpaTenantConfigurationManagement; import org.eclipse.hawkbit.repository.jpa.JpaTenantStatsManagement; import org.eclipse.hawkbit.repository.jpa.aspects.ExceptionMappingAspectHandler; +import org.eclipse.hawkbit.repository.jpa.autoassign.AutoAssignChecker; +import org.eclipse.hawkbit.repository.jpa.autoassign.AutoAssignScheduler; import org.eclipse.hawkbit.repository.jpa.configuration.MultiTenantJpaTransactionManager; import org.eclipse.hawkbit.repository.jpa.model.helper.AfterTransactionCommitExecutorHolder; import org.eclipse.hawkbit.repository.jpa.model.helper.CacheManagerHolder; @@ -390,4 +392,46 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration { public EntityFactory entityFactory() { return new JpaEntityFactory(); } + + /** + * {@link AutoAssignChecker} bean. + * + * @param targetFilterQueryManagement + * to get all target filter queries + * @param targetManagement + * to get targets + * @param deploymentManagement + * to assign distribution sets to targets + * @param transactionManager + * to run transactions + * @return a new {@link AutoAssignChecker} + */ + @Bean + @ConditionalOnMissingBean + public AutoAssignChecker autoAssignChecker(TargetFilterQueryManagement targetFilterQueryManagement, + TargetManagement targetManagement, DeploymentManagement deploymentManagement, + PlatformTransactionManager transactionManager) { + return new AutoAssignChecker(targetFilterQueryManagement, targetManagement, deploymentManagement, + transactionManager); + } + + /** + * {@link AutoAssignScheduler} bean. + * + * @param tenantAware + * to run as specific tenant + * @param systemManagement + * to find all tenants + * @param systemSecurityContext + * to run as system + * @param autoAssignChecker + * to run a check as tenant + * @return a new {@link AutoAssignChecker} + */ + @Bean + @ConditionalOnMissingBean + public AutoAssignScheduler autoAssignScheduler(TenantAware tenantAware, SystemManagement systemManagement, + SystemSecurityContext systemSecurityContext, AutoAssignChecker autoAssignChecker) { + return new AutoAssignScheduler(tenantAware, systemManagement, systemSecurityContext, autoAssignChecker); + } } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignChecker.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignChecker.java index ed001f481..1b78ee7ee 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignChecker.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignChecker.java @@ -23,68 +23,79 @@ import org.eclipse.hawkbit.repository.model.TargetFilterQuery; import org.eclipse.hawkbit.repository.model.TargetWithActionType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; -import org.springframework.stereotype.Component; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.DefaultTransactionDefinition; import org.springframework.transaction.support.TransactionTemplate; import javax.persistence.PersistenceException; /** - * Checks if targets need a new distribution set (DS) based on the target filter queries and - * assigns the new DS when necessary. - * First all target filter queries are listed. For every target filter query (TFQ) the auto assign DS - * is retrieved. - * All targets get listed per target filter query, that match the TFQ and that don't have the - * auto assign DS in their action history. + * Checks if targets need a new distribution set (DS) based on the target filter + * queries and assigns the new DS when necessary. First all target filter + * queries are listed. For every target filter query (TFQ) the auto assign DS is + * retrieved. All targets get listed per target filter query, that match the TFQ + * and that don't have the auto assign DS in their action history. */ -@Component public class AutoAssignChecker { private static final Logger LOGGER = LoggerFactory.getLogger(AutoAssignChecker.class); - @Autowired - private TargetFilterQueryManagement targetFilterQueryManagement; + private final TargetFilterQueryManagement targetFilterQueryManagement; - @Autowired - private TargetManagement targetManagement; + private final TargetManagement targetManagement; - @Autowired - private DeploymentManagement deploymentManagement; - - @Autowired - private PlatformTransactionManager transactionManager; - - private TransactionTemplate transactionTemplate; + private final DeploymentManagement deploymentManagement; + private final TransactionTemplate transactionTemplate; /** - * Maximum for target filter queries with auto assign DS - * Maximum for targets that are fetched in one turn + * Maximum for target filter queries with auto assign DS Maximum for targets + * that are fetched in one turn */ private static final int PAGE_SIZE = 1000; /** - * The message which is added to the action status when a distribution set is assigned - * to an target. First %s is the name of the target filter. + * The message which is added to the action status when a distribution set + * is assigned to an target. First %s is the name of the target filter. */ private static final String ACTION_MESSAGE = "Auto assignment by target filter: %s"; /** - * Checks all target filter queries with an auto assign distribution set - * and triggers the check and assignment to targets that don't have the design DS yet + * Instantiates a new auto assign checker + * + * @param targetFilterQueryManagement + * to get all target filter queries + * @param targetManagement + * to get targets + * @param deploymentManagement + * to assign distribution sets to targets + * @param transactionManager + * to run transactions */ + public AutoAssignChecker(TargetFilterQueryManagement targetFilterQueryManagement, TargetManagement targetManagement, + DeploymentManagement deploymentManagement, PlatformTransactionManager transactionManager) { + this.targetFilterQueryManagement = targetFilterQueryManagement; + this.targetManagement = targetManagement; + this.deploymentManagement = deploymentManagement; + + final DefaultTransactionDefinition def = new DefaultTransactionDefinition(); + def.setName("autoAssignDSToTargets"); + def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); + transactionTemplate = new TransactionTemplate(transactionManager, def); + } + + /** + * Checks all target filter queries with an auto assign distribution set and + * triggers the check and assignment to targets that don't have the design + * DS yet + */ + @Transactional(propagation = Propagation.REQUIRES_NEW) public void check() { - if(transactionTemplate == null) { - final DefaultTransactionDefinition def = new DefaultTransactionDefinition(); - def.setName("autoAssignDSToTargets"); - def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); - transactionTemplate = new TransactionTemplate(transactionManager, def); - } PageRequest pageRequest = new PageRequest(0, PAGE_SIZE); @@ -99,9 +110,9 @@ public class AutoAssignChecker { /** * Fetches the distribution set, gets all controllerIds and assigns the DS - * to them. - * Catches PersistenceException and own exceptions derived from AbstractServerRtException - * + * to them. Catches PersistenceException and own exceptions derived from + * AbstractServerRtException + * * @param targetFilterQuery * the target filter query */ @@ -124,7 +135,7 @@ public class AutoAssignChecker { /** * Runs one page of target assignments within a dedicated transaction - * + * * @param targetFilterQuery * the target filter query * @param dsId @@ -144,8 +155,9 @@ public class AutoAssignChecker { } /** - * Gets all matching targets with the designated action from the target management - * + * Gets all matching targets with the designated action from the target + * management + * * @param targetFilterQuery * the query the targets have to match * @param dsId @@ -155,14 +167,13 @@ public class AutoAssignChecker { * maximum amount of targets to retrieve * @return list of targets with action type */ - private List getTargetsWithActionType(TargetFilterQuery targetFilterQuery, Long dsId, int count) { + private List getTargetsWithActionType(TargetFilterQuery targetFilterQuery, Long dsId, + int count) { Page targets = targetManagement.findAllTargetsByTargetFilterQueryAndNonDS(new PageRequest(0, count), dsId, targetFilterQuery); - return targets.getContent().stream() - .map(t -> new TargetWithActionType(t.getControllerId(), Action.ActionType.FORCED, - RepositoryModelConstants.NO_FORCE_TIME)) - .collect(Collectors.toList()); + return targets.getContent().stream().map(t -> new TargetWithActionType(t.getControllerId(), + Action.ActionType.FORCED, RepositoryModelConstants.NO_FORCE_TIME)).collect(Collectors.toList()); } } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignScheduler.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignScheduler.java index 87c5a48a7..e677f889b 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignScheduler.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignScheduler.java @@ -16,16 +16,13 @@ import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.tenancy.TenantAware; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Profile; import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Component; /** * Scheduler to check target filters for auto assignment of distribution sets */ -@Component // don't active the auto assign scheduler in test, otherwise it is hard to test @Profile("!test") @EnableConfigurationProperties(AutoAssignProperties.class) @@ -33,17 +30,33 @@ public class AutoAssignScheduler { private static final Logger LOGGER = LoggerFactory.getLogger(AutoAssignScheduler.class); - @Autowired - private TenantAware tenantAware; + private final TenantAware tenantAware; - @Autowired - private SystemManagement systemManagement; + private final SystemManagement systemManagement; - @Autowired - private SystemSecurityContext systemSecurityContext; + private final SystemSecurityContext systemSecurityContext; - @Autowired - private AutoAssignChecker autoAssignChecker; + private final AutoAssignChecker autoAssignChecker; + + /** + * Instantiates a new AutoAssignScheduler + * + * @param tenantAware + * to run as specific tenant + * @param systemManagement + * to find all tenants + * @param systemSecurityContext + * to run as system + * @param autoAssignChecker + * to run a check as tenant + */ + public AutoAssignScheduler(TenantAware tenantAware, SystemManagement systemManagement, + SystemSecurityContext systemSecurityContext, AutoAssignChecker autoAssignChecker) { + this.tenantAware = tenantAware; + this.systemManagement = systemManagement; + this.systemSecurityContext = systemSecurityContext; + this.autoAssignChecker = autoAssignChecker; + } /** * Scheduler method called by the spring-async mechanism. Retrieves all From 97ab881d6e9bebd2b4b4ade32d6b0b23c44d14e3 Mon Sep 17 00:00:00 2001 From: Dominik Herbst Date: Wed, 5 Oct 2016 12:46:21 +0200 Subject: [PATCH 17/28] Code improvements Signed-off-by: Dominik Herbst --- ...lerPreAuthenticatedSecurityHeaderFilter.java | 17 +++++------------ ...hTokenSourceTrustAuthenticationProvider.java | 6 +++++- ...reAuthenticatedSecurityHeaderFilterTest.java | 3 --- 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilter.java b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilter.java index 6965afe3f..a536b319d 100644 --- a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilter.java +++ b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilter.java @@ -10,7 +10,7 @@ package org.eclipse.hawkbit.security; import java.util.Arrays; import java.util.List; -import java.util.Set; +import java.util.stream.Collectors; import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken; import org.eclipse.hawkbit.repository.TenantConfigurationManagement; @@ -19,15 +19,11 @@ import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.collect.Sets; - /** * An pre-authenticated processing filter which extracts the principal from a * request URI and the credential from a request header in a the * {@link TenantSecurityToken}. * - * - * */ public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractControllerAuthenticationFilter { @@ -112,12 +108,10 @@ public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractCont controllerId = secruityToken.getHeader(caCommonNameHeader); } - List knownHashes = splitMultiHash(authorityNameConfigurationValue); + List knownHashes = splitMultiHashBySemicolon(authorityNameConfigurationValue); - Set multiHashes = Sets.newHashSetWithExpectedSize(knownHashes.size()); final String cntlId = controllerId; - knownHashes.forEach(hashItem -> multiHashes.add(new HeaderAuthentication(cntlId, hashItem))); - return multiHashes; + return knownHashes.stream().map(hashItem -> new HeaderAuthentication(cntlId, hashItem)).collect(Collectors.toSet()); } /** @@ -128,8 +122,7 @@ public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractCont */ private String getIssuerHashHeader(final TenantSecurityToken secruityToken, final String knownIssuerHashes) { // there may be several knownIssuerHashes configured for the tenant - // separated by a semicolon - List knownHashes = splitMultiHash(knownIssuerHashes); + List knownHashes = splitMultiHashBySemicolon(knownIssuerHashes); // iterate over the headers until we get a null header. int iHeader = 1; @@ -162,7 +155,7 @@ public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractCont } } - private static List splitMultiHash(String knownIssuerHashes) { + private static List splitMultiHashBySemicolon(String knownIssuerHashes) { return Arrays.asList(knownIssuerHashes.split(";")); } } diff --git a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthTokenSourceTrustAuthenticationProvider.java b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthTokenSourceTrustAuthenticationProvider.java index 0809f6691..ffe1b7377 100644 --- a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthTokenSourceTrustAuthenticationProvider.java +++ b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthTokenSourceTrustAuthenticationProvider.java @@ -123,14 +123,18 @@ public class PreAuthTokenSourceTrustAuthenticationProvider implements Authentica * certificate. * * @param principal + * the {@link HeaderAuthentication} from the header * @param credentials + * a single {@link HeaderAuthentication} or a Collection of + * HeaderAuthentication * @param tokenDetails + * authentication details * @return true if authentication succeeded, otherwise * false */ private boolean calculateAuthenticationSuccess(Object principal, Object credentials, Object tokenDetails) { boolean successAuthentication = false; - if (Collection.class.isAssignableFrom(credentials.getClass())) { + if (credentials instanceof Collection) { final Collection multiValueCredentials = (Collection) credentials; if (multiValueCredentials.contains(principal)) { successAuthentication = checkSourceIPAddressIfNeccessary(tokenDetails); diff --git a/hawkbit-security-integration/src/test/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilterTest.java b/hawkbit-security-integration/src/test/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilterTest.java index 1275e44b0..15e9c2055 100644 --- a/hawkbit-security-integration/src/test/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilterTest.java +++ b/hawkbit-security-integration/src/test/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilterTest.java @@ -8,8 +8,6 @@ */ package org.eclipse.hawkbit.security; - -//import static org.junit.Assert.*; import static org.fest.assertions.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.mockito.Matchers.eq; @@ -105,7 +103,6 @@ public class ControllerPreAuthenticatedSecurityHeaderFilterTest { eq(TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_AUTHORITY_NAME), eq(String.class))) .thenReturn(CONFIG_VALUE_MULTI_HASH); assertThat(underTest.getPreAuthenticatedPrincipal(securityToken)).isNull(); - ; } @Test From f139c8dafd03e0b78033f3a7c53782e5cfc8e501 Mon Sep 17 00:00:00 2001 From: Michael Hirsch Date: Thu, 6 Oct 2016 09:02:45 +0200 Subject: [PATCH 18/28] skip example maven deploy, due the size of the example app Signed-off-by: Michael Hirsch --- examples/pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/examples/pom.xml b/examples/pom.xml index 85dd70651..66bce5fc1 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -21,6 +21,11 @@ hawkbit-examples-parent hawkBit-example :: Parent pom + + + + true + hawkbit-device-simulator From c018fdd039c0d81efdaf28d20765ed48fa557f3b Mon Sep 17 00:00:00 2001 From: Michael Hirsch Date: Thu, 6 Oct 2016 09:39:14 +0200 Subject: [PATCH 19/28] remove examples from maven-modules. Signed-off-by: Michael Hirsch --- README.md | 5 +++++ pom.xml | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 778391f32..0157e089e 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,11 @@ $ git clone https://github.com/eclipse/hawkbit.git $ cd hawkbit $ mvn clean install ``` +Build hawkBit examples. The examples are not included in the maven-reactor build. +``` +$ cd hawkbit/examples +$ mvn clean install +``` #### Start hawkBit example app [Example Application](examples/hawkbit-example-app) ``` diff --git a/pom.xml b/pom.xml index 2084b92f6..7106332f9 100644 --- a/pom.xml +++ b/pom.xml @@ -42,7 +42,6 @@ hawkbit-autoconfigure hawkbit-cache-redis hawkbit-test-report - examples From 3baacf791505d3d03dfb6312a4324830aa75f32a Mon Sep 17 00:00:00 2001 From: Michael Hirsch Date: Thu, 6 Oct 2016 10:20:10 +0200 Subject: [PATCH 20/28] enable test-report generation via profile Signed-off-by: Michael Hirsch --- hawkbit-test-report/pom.xml | 115 ++++++++++++++++++------------------ 1 file changed, 58 insertions(+), 57 deletions(-) diff --git a/hawkbit-test-report/pom.xml b/hawkbit-test-report/pom.xml index 3e9182f0a..77e416d2c 100644 --- a/hawkbit-test-report/pom.xml +++ b/hawkbit-test-report/pom.xml @@ -8,9 +8,7 @@ http://www.eclipse.org/legal/epl-v10.html --> - - + 4.0.0 org.eclipse.hawkbit @@ -22,60 +20,63 @@ pom - - - - maven-resources-plugin - 2.7 - - - copy-resources - verify - - copy-resources - + + + generateTestReport + + + + maven-resources-plugin + + + copy-resources + verify + + copy-resources + + + ${basedir}/target/allure-results + + + ${basedir}/.. + false + + **/target/allure-results/*.xml + + + + ${basedir} + false + + placeholder.txt + + + + + + + + + org.apache.maven.plugins + maven-assembly-plugin - ${basedir}/target/allure-results - - - ${basedir}/.. - false - - **/target/allure-results/*.xml - - - - ${basedir} - false - - placeholder.txt - - - + + src/main/assembly/test-report.xml + + false - - - - - org.apache.maven.plugins - maven-assembly-plugin - 2.5.3 - - - src/main/assembly/test-report.xml - - false - - - - create-report-zip-assembly - verify - - single - - - - - - + + + create-report-zip-assembly + verify + + single + + + + + + + + \ No newline at end of file From c427503c19b6565808fc0887f78f9dafa4d14dad Mon Sep 17 00:00:00 2001 From: Michael Hirsch Date: Thu, 6 Oct 2016 11:25:04 +0200 Subject: [PATCH 21/28] change scm information for releasing hawkbit Signed-off-by: Michael Hirsch --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7106332f9..4156620ff 100644 --- a/pom.xml +++ b/pom.xml @@ -148,7 +148,7 @@ - scm:git:https://github.com/eclipse/hawkbit.git + scm:git:git@github.com:eclipse/hawkbit.git scm:git:https://github.com/eclipse/hawkbit.git https://github.com/eclipse/hawkbit.git From e469b8fad224bfa299ff51747168043559f734d4 Mon Sep 17 00:00:00 2001 From: kaizimmerm Date: Thu, 6 Oct 2016 16:14:23 +0200 Subject: [PATCH 22/28] reenabled examples on Bosch fork to allo IoT SP release. Signed-off-by: kaizimmerm --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 7106332f9..2084b92f6 100644 --- a/pom.xml +++ b/pom.xml @@ -42,6 +42,7 @@ hawkbit-autoconfigure hawkbit-cache-redis hawkbit-test-report + examples From 0f9da64c7e488513b031d6081469fe5e57daf3d6 Mon Sep 17 00:00:00 2001 From: Michael Hirsch Date: Fri, 7 Oct 2016 08:26:46 +0200 Subject: [PATCH 23/28] introduce nonExampleApp profile for building examples without boot apps Signed-off-by: Michael Hirsch --- README.md | 5 ----- examples/pom.xml | 41 ++++++++++++++++++++++++++--------------- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 0157e089e..778391f32 100644 --- a/README.md +++ b/README.md @@ -33,11 +33,6 @@ $ git clone https://github.com/eclipse/hawkbit.git $ cd hawkbit $ mvn clean install ``` -Build hawkBit examples. The examples are not included in the maven-reactor build. -``` -$ cd hawkbit/examples -$ mvn clean install -``` #### Start hawkBit example app [Example Application](examples/hawkbit-example-app) ``` diff --git a/examples/pom.xml b/examples/pom.xml index 66bce5fc1..5f8718e0a 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -8,8 +8,7 @@ http://www.eclipse.org/legal/epl-v10.html --> - + 4.0.0 @@ -21,20 +20,32 @@ hawkbit-examples-parent hawkBit-example :: Parent pom - - - - true - - - hawkbit-device-simulator - hawkbit-example-app - hawkbit-example-core-feign-client - hawkbit-example-ddi-feign-client - hawkbit-example-mgmt-feign-client - hawkbit-example-mgmt-simulator - + + + noExampleApp + + hawkbit-example-core-feign-client + hawkbit-example-ddi-feign-client + hawkbit-example-mgmt-feign-client + hawkbit-example-mgmt-simulator + + + + default + + true + + + hawkbit-device-simulator + hawkbit-example-app + hawkbit-example-core-feign-client + hawkbit-example-ddi-feign-client + hawkbit-example-mgmt-feign-client + hawkbit-example-mgmt-simulator + + + From a3e53596b7dc45be48685f8194abae735a3190df Mon Sep 17 00:00:00 2001 From: Kai Zimmermann Date: Wed, 12 Oct 2016 12:11:29 +0200 Subject: [PATCH 24/28] Add maven central badge Signed-off-by: kaizimmerm --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 778391f32..b9e826898 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [hawkBit](https://projects.eclipse.org/projects/iot.hawkbit) is an domain independent back end solution for rolling out software updates to constrained edge devices as well as more powerful controllers and gateways connected to IP based networking infrastructure. Build: [![Circle CI](https://circleci.com/gh/eclipse/hawkbit.svg?style=shield)](https://circleci.com/gh/eclipse/hawkbit) -[![SonarQuality](https://sonar.eu-gb.mybluemix.net/api/badges/gate?key=org.eclipse.hawkbit:hawkbit-parent)](https://sonar.eu-gb.mybluemix.net) +[![SonarQuality](https://sonar.eu-gb.mybluemix.net/api/badges/gate?key=org.eclipse.hawkbit:hawkbit-parent)](https://sonar.eu-gb.mybluemix.net)[![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.eclipse.hawkbit/hawkbit-parent/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.eclipse.hawkbit/hawkbit-parent) # Documentation @@ -78,4 +78,4 @@ $ java -jar ./examples/hawkbit-example-mgmt-simulator/target/hawkbit-example-mgm * `hawkbit-security-core` : Core security elements. * `hawkbit-security-integration` : Security integration elements to integrate security into hawkBit. * `hawkbit-test-report` : Test reports -* `hawkbit-ui` : Vaadin UI. \ No newline at end of file +* `hawkbit-ui` : Vaadin UI. From 771da69fca83baa8138053d86556973feb9a38ce Mon Sep 17 00:00:00 2001 From: Dominik Herbst Date: Wed, 12 Oct 2016 14:40:03 +0200 Subject: [PATCH 25/28] Fixed SpecificationsBuilder to support immutable Collections and added a test for it Signed-off-by: Dominik Herbst --- .../specifications/SpecificationsBuilder.java | 5 +- .../SpecificationsBuilderTest.java | 106 ++++++++++++++++++ 2 files changed, 108 insertions(+), 3 deletions(-) create mode 100644 hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/specifications/SpecificationsBuilderTest.java diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/SpecificationsBuilder.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/SpecificationsBuilder.java index 005942719..095269622 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/SpecificationsBuilder.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/SpecificationsBuilder.java @@ -28,7 +28,7 @@ public final class SpecificationsBuilder { * where clause. * * @param specList - * all specification wich will combine + * all specification which will combine * @return if the given specification list is empty */ public static Specifications combineWithAnd(final List> specList) { @@ -36,8 +36,7 @@ public final class SpecificationsBuilder { return null; } Specifications specs = Specifications.where(specList.get(0)); - specList.remove(0); - for (final Specification specification : specList) { + for (final Specification specification : specList.subList(1, specList.size())) { specs = specs.and(specification); } return specs; diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/specifications/SpecificationsBuilderTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/specifications/SpecificationsBuilderTest.java new file mode 100644 index 000000000..aeb28fcae --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/specifications/SpecificationsBuilderTest.java @@ -0,0 +1,106 @@ +/** + * Copyright (c) 2015 Bosch Software Innovations 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 + */ +package org.eclipse.hawkbit.repository.jpa.specifications; + +import org.junit.Test; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.data.jpa.domain.Specifications; +import ru.yandex.qatools.allure.annotations.Description; +import ru.yandex.qatools.allure.annotations.Features; +import ru.yandex.qatools.allure.annotations.Stories; + +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Expression; +import javax.persistence.criteria.Path; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@Features("Unit Tests - Repository") +@Stories("Specifications builder") +public class SpecificationsBuilderTest { + + @Test + @Description("Test the combination of specs on an empty list which returns null") + public void combineWithAndEmptyList() { + List> specList = Collections.emptyList(); + assertThat(SpecificationsBuilder.combineWithAnd(specList)).isNull(); + } + + @Test + @Description("Test the combination of specs on an immutable list with one entry") + public void combineWithAndSingleImmutableList() { + Specification spec = (root, query, cb) -> cb.equal(root.get("field1"), "testValue"); + List> specList = Collections.singletonList(spec); + Specifications specifications = SpecificationsBuilder.combineWithAnd(specList); + assertThat(specifications).as("Specifications").isNotNull(); + + // mocks to call toPredicate on specifications + CriteriaBuilder criteriaBuilder = mock(CriteriaBuilder.class); + final Path field1 = mock(Path.class); + final Predicate equalPredicate = mock(Predicate.class); + final CriteriaQuery query = mock(CriteriaQuery.class); + final Root root = mock(Root.class); + + when(criteriaBuilder.equal(any(Expression.class), anyString())).thenReturn(equalPredicate); + when(root.get("field1")).thenReturn(field1); + + Predicate predicate = specifications.toPredicate(root, query, criteriaBuilder); + + assertThat(predicate).isEqualTo(equalPredicate); + + } + + + @Test + @Description("Test the combination of specs on a list with multiple entries") + public void combineWithAndList() { + Specification spec1 = (root, query, cb) -> cb.equal(root.get("field1"), "testValue1"); + Specification spec2 = (root, query, cb) -> cb.equal(root.get("field2"), "testValue2"); + + List> specList = new ArrayList<>(2); + specList.add(spec1); + specList.add(spec2); + + Specifications specifications = SpecificationsBuilder.combineWithAnd(specList); + assertThat(specifications).as("Specifications").isNotNull(); + + // mocks to call toPredicate on specifications + CriteriaBuilder criteriaBuilder = mock(CriteriaBuilder.class); + final Path field1 = mock(Path.class); + final Path field2 = mock(Path.class); + final Predicate equalPredicate1 = mock(Predicate.class); + final Predicate equalPredicate2 = mock(Predicate.class); + final Predicate combinedPredicate = mock(Predicate.class); + final CriteriaQuery query = mock(CriteriaQuery.class); + final Root root = mock(Root.class); + + when(criteriaBuilder.equal(any(Path.class), eq("testValue1"))).thenReturn(equalPredicate1); + when(criteriaBuilder.equal(any(Path.class), eq("testValue2"))).thenReturn(equalPredicate2); + when(criteriaBuilder.and(eq(equalPredicate1), eq(equalPredicate2))).thenReturn(combinedPredicate); + when(root.get("field1")).thenReturn(field1); + when(root.get("field2")).thenReturn(field2); + + Predicate predicate = specifications.toPredicate(root, query, criteriaBuilder); + + assertThat(predicate).as("Combined predicate").isEqualTo(combinedPredicate); + + } + +} \ No newline at end of file From f768c16e947a59ef2ce9dad63fa3b8c1447e0cdd Mon Sep 17 00:00:00 2001 From: Dominik Herbst Date: Wed, 12 Oct 2016 14:41:41 +0200 Subject: [PATCH 26/28] Set initial capacity for new ArrayList or replaced the usage of ArrayList. Signed-off-by: Dominik Herbst --- .../repository/TargetFilterQueryManagement.java | 2 +- .../jpa/JpaTargetFilterQueryManagement.java | 15 ++++++++------- .../repository/jpa/JpaTargetManagement.java | 3 +-- .../DistributionSetSelectTable.java | 11 ++++------- .../ui/filtermanagement/TargetFilterTable.java | 12 ++++-------- 5 files changed, 18 insertions(+), 25 deletions(-) diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetFilterQueryManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetFilterQueryManagement.java index bd7bec4e5..27bb32b25 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetFilterQueryManagement.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetFilterQueryManagement.java @@ -175,4 +175,4 @@ public interface TargetFilterQueryManagement { */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET) TargetFilterQuery updateTargetFilterQuery(@NotNull TargetFilterQuery targetFilterQuery); -} \ No newline at end of file +} diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetFilterQueryManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetFilterQueryManagement.java index 27f803a00..ab26ed60f 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetFilterQueryManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetFilterQueryManagement.java @@ -9,6 +9,7 @@ package org.eclipse.hawkbit.repository.jpa; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import org.eclipse.hawkbit.repository.TargetFields; @@ -83,18 +84,18 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme @Override public Page findTargetFilterQueryByName(final Pageable pageable, final String name) { - final List> specList = new ArrayList<>(); + List> specList = Collections.emptyList(); if (!Strings.isNullOrEmpty(name)) { - specList.add(TargetFilterQuerySpecification.likeName(name)); + specList = Collections.singletonList(TargetFilterQuerySpecification.likeName(name)); } return convertPage(findTargetFilterQueryByCriteriaAPI(pageable, specList), pageable); } @Override public Page findTargetFilterQueryByFilter(@NotNull Pageable pageable, String rsqlFilter) { - final List> specList = new ArrayList<>(); + List> specList = Collections.emptyList(); if (!Strings.isNullOrEmpty(rsqlFilter)) { - specList.add(RSQLUtility.parse(rsqlFilter, TargetFilterQueryFields.class)); + specList = Collections.singletonList(RSQLUtility.parse(rsqlFilter, TargetFilterQueryFields.class)); } return convertPage(findTargetFilterQueryByCriteriaAPI(pageable, specList), pageable); } @@ -108,7 +109,7 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme @Override public Page findTargetFilterQueryByAutoAssignDS(@NotNull Pageable pageable, DistributionSet distributionSet, String rsqlFilter) { - final List> specList = new ArrayList<>(); + final List> specList = new ArrayList<>(2); if (distributionSet != null) { specList.add(TargetFilterQuerySpecification.byAutoAssignDS(distributionSet)); } @@ -120,8 +121,8 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme @Override public Page findTargetFilterQueryWithAutoAssignDS(@NotNull Pageable pageable) { - final List> specList = new ArrayList<>(); - specList.add(TargetFilterQuerySpecification.withAutoAssignDS()); + final List> specList = Collections + .singletonList(TargetFilterQuerySpecification.withAutoAssignDS()); return convertPage(findTargetFilterQueryByCriteriaAPI(pageable, specList), pageable); } 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 4fb68a2eb..02f4f1d5c 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 @@ -579,9 +579,8 @@ public class JpaTargetManagement implements TargetManagement { @Override public Long countTargetsByTargetFilterQueryAndNonDS(Long distributionSetId, @NotNull TargetFilterQuery targetFilterQuery) { final Specification spec = RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class); - final List> specList = new ArrayList<>(); + final List> specList = new ArrayList<>(2); specList.add(spec); - specList.add(TargetSpecifications.hasNotDistributionSetInActions(distributionSetId)); return countByCriteriaAPI(specList); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/DistributionSetSelectTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/DistributionSetSelectTable.java index 516a5449b..82d41a616 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/DistributionSetSelectTable.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/DistributionSetSelectTable.java @@ -118,7 +118,7 @@ public class DistributionSetSelectTable extends Table { } private List getVisbleColumns() { - final List columnList = new ArrayList<>(); + final List columnList = new ArrayList<>(2); columnList.add(new TableColumn(SPUILabelDefinitions.NAME, i18n.get("header.name"), 0.6F)); columnList.add(new TableColumn(SPUILabelDefinitions.VAR_VERSION, i18n.get("header.version"), 0.4F)); return columnList; @@ -126,14 +126,11 @@ public class DistributionSetSelectTable extends Table { } private void setColumnProperties() { - final List columnList = getVisbleColumns(); - final List swColumnIds = new ArrayList<>(); - for (final TableColumn column : columnList) { + setVisibleColumns(getVisbleColumns().stream().map(column -> { setColumnHeader(column.getColumnPropertyId(), column.getColumnHeader()); setColumnExpandRatio(column.getColumnPropertyId(), column.getExpandRatio()); - swColumnIds.add(column.getColumnPropertyId()); - } - setVisibleColumns(swColumnIds.toArray()); + return column.getColumnPropertyId(); + }).toArray()); } private Map prepareQueryConfigFilters() { diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TargetFilterTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TargetFilterTable.java index b4833bdbe..6aefa0b75 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TargetFilterTable.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TargetFilterTable.java @@ -10,7 +10,6 @@ package org.eclipse.hawkbit.ui.filtermanagement; import java.util.ArrayList; import java.util.Date; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -154,7 +153,7 @@ public class TargetFilterTable extends Table { } private List getVisbleColumns() { - final List columnList = new ArrayList<>(); + final List columnList = new ArrayList<>(7); columnList.add(new TableColumn(SPUILabelDefinitions.NAME, i18n.get("header.name"), 0.2F)); columnList.add(new TableColumn(SPUILabelDefinitions.VAR_CREATED_USER, i18n.get("header.createdBy"), 0.1F)); columnList.add(new TableColumn(SPUILabelDefinitions.VAR_CREATED_DATE, i18n.get("header.createdDate"), 0.2F)); @@ -287,14 +286,11 @@ public class TargetFilterTable extends Table { } private void setColumnProperties() { - final List columnList = getVisbleColumns(); - final List swColumnIds = new ArrayList<>(); - for (final TableColumn column : columnList) { + setVisibleColumns(getVisbleColumns().stream().map(column -> { setColumnHeader(column.getColumnPropertyId(), column.getColumnHeader()); setColumnExpandRatio(column.getColumnPropertyId(), column.getExpandRatio()); - swColumnIds.add(column.getColumnPropertyId()); - } - setVisibleColumns(swColumnIds.toArray()); + return column.getColumnPropertyId(); + }).toArray()); } } From 05cebdba54aacc40096f7874f2a7d79678057e1a Mon Sep 17 00:00:00 2001 From: Dominik Herbst Date: Wed, 12 Oct 2016 15:43:20 +0200 Subject: [PATCH 27/28] Used HeaderAuthentication for getPreAuthenticatedPrincipal. Improved test quality. Signed-off-by: Dominik Herbst --- ...bstractControllerAuthenticationFilter.java | 6 --- ...rPreAuthenticatedSecurityHeaderFilter.java | 2 +- .../security/PreAuthentificationFilter.java | 2 +- ...AuthenticatedSecurityHeaderFilterTest.java | 53 +++++++++---------- 4 files changed, 26 insertions(+), 37 deletions(-) diff --git a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/AbstractControllerAuthenticationFilter.java b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/AbstractControllerAuthenticationFilter.java index cf64e6c59..56baf3bfc 100644 --- a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/AbstractControllerAuthenticationFilter.java +++ b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/AbstractControllerAuthenticationFilter.java @@ -46,12 +46,6 @@ public abstract class AbstractControllerAuthenticationFilter implements PreAuthe return tenantAware.runAsTenant(secruityToken.getTenant(), configurationKeyTenantRunner); } - @Override - public abstract Object getPreAuthenticatedPrincipal(TenantSecurityToken secruityToken); - - @Override - public abstract Object getPreAuthenticatedCredentials(TenantSecurityToken secruityToken); - private final class SecurityConfigurationKeyTenantRunner implements TenantAware.TenantRunner { @Override public Boolean run() { diff --git a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilter.java b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilter.java index a536b319d..fae0b53ad 100644 --- a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilter.java +++ b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilter.java @@ -77,7 +77,7 @@ public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractCont } @Override - public Object getPreAuthenticatedPrincipal(final TenantSecurityToken secruityToken) { + public HeaderAuthentication getPreAuthenticatedPrincipal(final TenantSecurityToken secruityToken) { // retrieve the common name header and the authority name header from // the http request and combine them together final String commonNameValue = secruityToken.getHeader(caCommonNameHeader); diff --git a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthentificationFilter.java b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthentificationFilter.java index 72f362fa0..f0801ff4d 100644 --- a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthentificationFilter.java +++ b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthentificationFilter.java @@ -36,7 +36,7 @@ public interface PreAuthentificationFilter { * the secruityToken * @return the extracted tenant and controller id */ - Object getPreAuthenticatedPrincipal(TenantSecurityToken secruityToken); + HeaderAuthentication getPreAuthenticatedPrincipal(TenantSecurityToken secruityToken); /** * Extract the principal credentials from the current secruityToken. diff --git a/hawkbit-security-integration/src/test/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilterTest.java b/hawkbit-security-integration/src/test/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilterTest.java index 15e9c2055..80979795f 100644 --- a/hawkbit-security-integration/src/test/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilterTest.java +++ b/hawkbit-security-integration/src/test/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilterTest.java @@ -46,10 +46,13 @@ public class ControllerPreAuthenticatedSecurityHeaderFilterTest { private SecurityContextTenantAware tenantAware = new SecurityContextTenantAware(); private static final String CA_COMMON_NAME = "ca-cn"; + private static final String CA_COMMON_NAME_VALUE = "box1"; private static final String X_SSL_ISSUER_HASH_1 = "X-Ssl-Issuer-Hash-1"; private static final String SINGLE_HASH = "hash1"; + private static final String SECOND_HASH = "hash2"; + private static final String UNKNOWN_HASH = "unknown"; private static final String MULTI_HASH = "hash1;hash2;hash3"; @@ -69,9 +72,7 @@ public class ControllerPreAuthenticatedSecurityHeaderFilterTest { @Test @Description("Tests the filter for issuer hash based authentication with a single known hash") public void testIssuerHashBasedAuthenticationWithSingleKnownHash() { - // prepare security token - final TenantSecurityToken securityToken = prepareSecurityToken(); - securityToken.getHeaders().put(X_SSL_ISSUER_HASH_1, SINGLE_HASH); + final TenantSecurityToken securityToken = prepareSecurityToken(SINGLE_HASH); // use single known hash when(tenantConfigurationManagementMock.getConfigurationValue( eq(TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_AUTHORITY_NAME), eq(String.class))) @@ -82,9 +83,7 @@ public class ControllerPreAuthenticatedSecurityHeaderFilterTest { @Test @Description("Tests the filter for issuer hash based authentication with multiple known hashes") public void testIssuerHashBasedAuthenticationWithMultipleKnownHashes() { - // prepare security token - final TenantSecurityToken securityToken = prepareSecurityToken(); - securityToken.getHeaders().put(X_SSL_ISSUER_HASH_1, SINGLE_HASH); + final TenantSecurityToken securityToken = prepareSecurityToken(SINGLE_HASH); // use multiple known hashes when(tenantConfigurationManagementMock.getConfigurationValue( eq(TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_AUTHORITY_NAME), eq(String.class))) @@ -95,9 +94,7 @@ public class ControllerPreAuthenticatedSecurityHeaderFilterTest { @Test @Description("Tests the filter for issuer hash based authentication with unknown hash") public void testIssuerHashBasedAuthenticationWithUnknownHash() { - // prepare security token - final TenantSecurityToken securityToken = prepareSecurityToken(); - securityToken.getHeaders().put(X_SSL_ISSUER_HASH_1, "unknown"); + final TenantSecurityToken securityToken = prepareSecurityToken(UNKNOWN_HASH); // use single known hash when(tenantConfigurationManagementMock.getConfigurationValue( eq(TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_AUTHORITY_NAME), eq(String.class))) @@ -108,39 +105,37 @@ public class ControllerPreAuthenticatedSecurityHeaderFilterTest { @Test @Description("Tests different values for issuer hash header and inspects the credentials") public void useDifferentValuesForIssuerHashHeader() { + final TenantSecurityToken securityToken1 = prepareSecurityToken(SINGLE_HASH); + final TenantSecurityToken securityToken2 = prepareSecurityToken(SECOND_HASH); - // prepare security token - TenantSecurityToken securityToken = prepareSecurityToken(); - securityToken.getHeaders().put(X_SSL_ISSUER_HASH_1, "hash1"); + final HeaderAuthentication expected1 = new HeaderAuthentication(CA_COMMON_NAME_VALUE, SINGLE_HASH); + final HeaderAuthentication expected2 = new HeaderAuthentication(CA_COMMON_NAME_VALUE, SECOND_HASH); when(tenantConfigurationManagementMock.getConfigurationValue( eq(TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_AUTHORITY_NAME), eq(String.class))) .thenReturn(CONFIG_VALUE_MULTI_HASH); - HeaderAuthentication expected = new HeaderAuthentication("box1", "hash1"); - Collection credentials = (Collection) underTest - .getPreAuthenticatedCredentials(securityToken); - assertThat(credentials.contains(expected)).isTrue(); + final Collection credentials1 = (Collection) underTest + .getPreAuthenticatedCredentials(securityToken1); + final Collection credentials2 = (Collection) underTest + .getPreAuthenticatedCredentials(securityToken2); - Object principal = underTest.getPreAuthenticatedPrincipal(securityToken); - assertEquals("hash1 expected in principal!", expected, principal); + Object principal1 = underTest.getPreAuthenticatedPrincipal(securityToken1); + Object principal2 = underTest.getPreAuthenticatedPrincipal(securityToken2); - securityToken = prepareSecurityToken(); - securityToken.getHeaders().put(X_SSL_ISSUER_HASH_1, "hash2"); - expected = new HeaderAuthentication("box1", "hash2"); - credentials = (Collection) underTest.getPreAuthenticatedCredentials(securityToken); - assertThat(credentials.contains(expected)).isTrue(); + assertThat(credentials1.contains(expected1)).isTrue(); + assertThat(credentials2.contains(expected2)).isTrue(); - principal = underTest.getPreAuthenticatedPrincipal(securityToken); - assertEquals("hash2 expected in principal!", expected, principal); + assertEquals("hash1 expected in principal!", expected1, principal1); + assertEquals("hash2 expected in principal!", expected2, principal2); } - private static TenantSecurityToken prepareSecurityToken() { - final TenantSecurityToken securityToken = new TenantSecurityToken("DEFAULT", "box1", + private static TenantSecurityToken prepareSecurityToken(String issuerHashHeaderValue) { + final TenantSecurityToken securityToken = new TenantSecurityToken("DEFAULT", CA_COMMON_NAME_VALUE, FileResource.createFileResourceBySha1("12345")); - securityToken.getHeaders().put(CA_COMMON_NAME, "box1"); - + securityToken.getHeaders().put(CA_COMMON_NAME, CA_COMMON_NAME_VALUE); + securityToken.getHeaders().put(X_SSL_ISSUER_HASH_1, issuerHashHeaderValue); return securityToken; } From 43511ed63e49745c088099c252e820e0f0ab1d71 Mon Sep 17 00:00:00 2001 From: kaizimmerm Date: Thu, 13 Oct 2016 09:17:24 +0200 Subject: [PATCH 28/28] Fixed test. Signed-off-by: kaizimmerm --- ...AuthenticatedSecurityHeaderFilterTest.java | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/hawkbit-security-integration/src/test/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilterTest.java b/hawkbit-security-integration/src/test/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilterTest.java index 80979795f..bfa13f71a 100644 --- a/hawkbit-security-integration/src/test/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilterTest.java +++ b/hawkbit-security-integration/src/test/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilterTest.java @@ -43,7 +43,7 @@ public class ControllerPreAuthenticatedSecurityHeaderFilterTest { @Mock private TenantSecurityToken tenantSecurityTokenMock; - private SecurityContextTenantAware tenantAware = new SecurityContextTenantAware(); + private final SecurityContextTenantAware tenantAware = new SecurityContextTenantAware(); private static final String CA_COMMON_NAME = "ca-cn"; private static final String CA_COMMON_NAME_VALUE = "box1"; @@ -57,16 +57,15 @@ public class ControllerPreAuthenticatedSecurityHeaderFilterTest { private static final String MULTI_HASH = "hash1;hash2;hash3"; private static final TenantConfigurationValue CONFIG_VALUE_SINGLE_HASH = TenantConfigurationValue - .builder().value(SINGLE_HASH).build(); + . builder().value(SINGLE_HASH).build(); private static final TenantConfigurationValue CONFIG_VALUE_MULTI_HASH = TenantConfigurationValue - .builder().value(MULTI_HASH).build(); + . builder().value(MULTI_HASH).build(); @Before public void before() { underTest = new ControllerPreAuthenticatedSecurityHeaderFilter(CA_COMMON_NAME, "X-Ssl-Issuer-Hash-%d", - tenantConfigurationManagementMock, - tenantAware, new SystemSecurityContext(tenantAware)); + tenantConfigurationManagementMock, tenantAware, new SystemSecurityContext(tenantAware)); } @Test @@ -120,8 +119,8 @@ public class ControllerPreAuthenticatedSecurityHeaderFilterTest { final Collection credentials2 = (Collection) underTest .getPreAuthenticatedCredentials(securityToken2); - Object principal1 = underTest.getPreAuthenticatedPrincipal(securityToken1); - Object principal2 = underTest.getPreAuthenticatedPrincipal(securityToken2); + final Object principal1 = underTest.getPreAuthenticatedPrincipal(securityToken1); + final Object principal2 = underTest.getPreAuthenticatedPrincipal(securityToken2); assertThat(credentials1.contains(expected1)).isTrue(); assertThat(credentials2.contains(expected2)).isTrue(); @@ -131,11 +130,11 @@ public class ControllerPreAuthenticatedSecurityHeaderFilterTest { } - private static TenantSecurityToken prepareSecurityToken(String issuerHashHeaderValue) { + private static TenantSecurityToken prepareSecurityToken(final String issuerHashHeaderValue) { final TenantSecurityToken securityToken = new TenantSecurityToken("DEFAULT", CA_COMMON_NAME_VALUE, FileResource.createFileResourceBySha1("12345")); - securityToken.getHeaders().put(CA_COMMON_NAME, CA_COMMON_NAME_VALUE); - securityToken.getHeaders().put(X_SSL_ISSUER_HASH_1, issuerHashHeaderValue); + securityToken.putHeader(CA_COMMON_NAME, CA_COMMON_NAME_VALUE); + securityToken.putHeader(X_SSL_ISSUER_HASH_1, issuerHashHeaderValue); return securityToken; }