Merge branch 'master' into feature_target_filtering_supports_overdue
Conflicts: hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/RepositoryApplicationConfiguration.java hawkbit-ui/src/main/resources/messages_de.properties hawkbit-ui/src/main/resources/messages_en.properties
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -38,6 +38,10 @@ local.properties
|
|||||||
.settings/
|
.settings/
|
||||||
.loadpath
|
.loadpath
|
||||||
|
|
||||||
|
# IDEA
|
||||||
|
*.iml
|
||||||
|
.idea
|
||||||
|
|
||||||
# External tool builders
|
# External tool builders
|
||||||
.externalToolBuilders/
|
.externalToolBuilders/
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
[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: [](https://circleci.com/gh/eclipse/hawkbit)
|
Build: [](https://circleci.com/gh/eclipse/hawkbit)
|
||||||
[](https://sonar.eu-gb.mybluemix.net)
|
[](https://sonar.eu-gb.mybluemix.net)[](https://maven-badges.herokuapp.com/maven-central/org.eclipse.hawkbit/hawkbit-parent)
|
||||||
|
|
||||||
# Documentation
|
# Documentation
|
||||||
|
|
||||||
@@ -78,4 +78,4 @@ $ java -jar ./examples/hawkbit-example-mgmt-simulator/target/hawkbit-example-mgm
|
|||||||
* `hawkbit-security-core` : Core security elements.
|
* `hawkbit-security-core` : Core security elements.
|
||||||
* `hawkbit-security-integration` : Security integration elements to integrate security into hawkBit.
|
* `hawkbit-security-integration` : Security integration elements to integrate security into hawkBit.
|
||||||
* `hawkbit-test-report` : Test reports
|
* `hawkbit-test-report` : Test reports
|
||||||
* `hawkbit-ui` : Vaadin UI.
|
* `hawkbit-ui` : Vaadin UI.
|
||||||
|
|||||||
@@ -8,8 +8,7 @@
|
|||||||
http://www.eclipse.org/legal/epl-v10.html
|
http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
|
||||||
-->
|
-->
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
@@ -22,14 +21,31 @@
|
|||||||
<name>hawkBit-example :: Parent</name>
|
<name>hawkBit-example :: Parent</name>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
<modules>
|
<profiles>
|
||||||
<module>hawkbit-device-simulator</module>
|
<profile>
|
||||||
<module>hawkbit-example-app</module>
|
<id>noExampleApp</id>
|
||||||
<module>hawkbit-example-core-feign-client</module>
|
<modules>
|
||||||
<module>hawkbit-example-ddi-feign-client</module>
|
<module>hawkbit-example-core-feign-client</module>
|
||||||
<module>hawkbit-example-mgmt-feign-client</module>
|
<module>hawkbit-example-ddi-feign-client</module>
|
||||||
<module>hawkbit-example-mgmt-simulator</module>
|
<module>hawkbit-example-mgmt-feign-client</module>
|
||||||
</modules>
|
<module>hawkbit-example-mgmt-simulator</module>
|
||||||
|
</modules>
|
||||||
|
</profile>
|
||||||
|
<profile>
|
||||||
|
<id>default</id>
|
||||||
|
<activation>
|
||||||
|
<activeByDefault>true</activeByDefault>
|
||||||
|
</activation>
|
||||||
|
<modules>
|
||||||
|
<module>hawkbit-device-simulator</module>
|
||||||
|
<module>hawkbit-example-app</module>
|
||||||
|
<module>hawkbit-example-core-feign-client</module>
|
||||||
|
<module>hawkbit-example-ddi-feign-client</module>
|
||||||
|
<module>hawkbit-example-mgmt-feign-client</module>
|
||||||
|
<module>hawkbit-example-mgmt-simulator</module>
|
||||||
|
</modules>
|
||||||
|
</profile>
|
||||||
|
</profiles>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|||||||
@@ -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<String> 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<String> subEntityAttribues) {
|
||||||
|
this.fieldName = fieldName;
|
||||||
|
this.mapField = mapField;
|
||||||
|
this.subEntityAttributes = subEntityAttribues;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getSubEntityAttributes() {
|
||||||
|
return subEntityAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isMap() {
|
||||||
|
return mapField;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFieldName() {
|
||||||
|
return fieldName;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,6 +21,8 @@ import org.springframework.security.authentication.InsufficientAuthenticationExc
|
|||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
|
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.Description;
|
||||||
import ru.yandex.qatools.allure.annotations.Features;
|
import ru.yandex.qatools.allure.annotations.Features;
|
||||||
import ru.yandex.qatools.allure.annotations.Stories;
|
import ru.yandex.qatools.allure.annotations.Stories;
|
||||||
@@ -45,8 +47,8 @@ public class PreAuthTokenSourceTrustAuthenticationProviderTest {
|
|||||||
public void principalAndCredentialsNotTheSameThrowsAuthenticationException() {
|
public void principalAndCredentialsNotTheSameThrowsAuthenticationException() {
|
||||||
final String principal = "controllerIdURL";
|
final String principal = "controllerIdURL";
|
||||||
final String credentials = "controllerIdHeader";
|
final String credentials = "controllerIdHeader";
|
||||||
final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(principal,
|
final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(
|
||||||
credentials);
|
principal, Lists.newArrayList(credentials));
|
||||||
token.setDetails(webAuthenticationDetailsMock);
|
token.setDetails(webAuthenticationDetailsMock);
|
||||||
|
|
||||||
// test, should throw authentication exception
|
// test, should throw authentication exception
|
||||||
@@ -64,11 +66,12 @@ public class PreAuthTokenSourceTrustAuthenticationProviderTest {
|
|||||||
public void principalAndCredentialsAreTheSameWithNoSourceIpCheckIsSuccessful() {
|
public void principalAndCredentialsAreTheSameWithNoSourceIpCheckIsSuccessful() {
|
||||||
final String principal = "controllerId";
|
final String principal = "controllerId";
|
||||||
final String credentials = "controllerId";
|
final String credentials = "controllerId";
|
||||||
final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(principal,
|
final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(
|
||||||
credentials);
|
principal, Lists.newArrayList(credentials));
|
||||||
token.setDetails(webAuthenticationDetailsMock);
|
token.setDetails(webAuthenticationDetailsMock);
|
||||||
|
|
||||||
final Authentication authenticate = underTestWithoutSourceIpCheck.authenticate(token);
|
final Authentication authenticate = underTestWithoutSourceIpCheck
|
||||||
|
.authenticate(token);
|
||||||
assertThat(authenticate.isAuthenticated()).isTrue();
|
assertThat(authenticate.isAuthenticated()).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,8 +81,8 @@ public class PreAuthTokenSourceTrustAuthenticationProviderTest {
|
|||||||
final String remoteAddress = "192.168.1.1";
|
final String remoteAddress = "192.168.1.1";
|
||||||
final String principal = "controllerId";
|
final String principal = "controllerId";
|
||||||
final String credentials = "controllerId";
|
final String credentials = "controllerId";
|
||||||
final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(principal,
|
final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(
|
||||||
credentials);
|
principal, Lists.newArrayList(credentials));
|
||||||
token.setDetails(webAuthenticationDetailsMock);
|
token.setDetails(webAuthenticationDetailsMock);
|
||||||
|
|
||||||
when(webAuthenticationDetailsMock.getRemoteAddress()).thenReturn(remoteAddress);
|
when(webAuthenticationDetailsMock.getRemoteAddress()).thenReturn(remoteAddress);
|
||||||
@@ -99,14 +102,15 @@ public class PreAuthTokenSourceTrustAuthenticationProviderTest {
|
|||||||
public void priniciapAndCredentialsAreTheSameAndSourceIpIsTrusted() {
|
public void priniciapAndCredentialsAreTheSameAndSourceIpIsTrusted() {
|
||||||
final String principal = "controllerId";
|
final String principal = "controllerId";
|
||||||
final String credentials = "controllerId";
|
final String credentials = "controllerId";
|
||||||
final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(principal,
|
final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(
|
||||||
credentials);
|
principal, Lists.newArrayList(credentials));
|
||||||
token.setDetails(webAuthenticationDetailsMock);
|
token.setDetails(webAuthenticationDetailsMock);
|
||||||
|
|
||||||
when(webAuthenticationDetailsMock.getRemoteAddress()).thenReturn(REQUEST_SOURCE_IP);
|
when(webAuthenticationDetailsMock.getRemoteAddress()).thenReturn(REQUEST_SOURCE_IP);
|
||||||
|
|
||||||
// test, should throw authentication exception
|
// test, should throw authentication exception
|
||||||
final Authentication authenticate = underTestWithSourceIpCheck.authenticate(token);
|
final Authentication authenticate = underTestWithSourceIpCheck
|
||||||
|
.authenticate(token);
|
||||||
assertThat(authenticate.isAuthenticated()).isTrue();
|
assertThat(authenticate.isAuthenticated()).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,8 +120,8 @@ public class PreAuthTokenSourceTrustAuthenticationProviderTest {
|
|||||||
"192.168.1.3" };
|
"192.168.1.3" };
|
||||||
final String principal = "controllerId";
|
final String principal = "controllerId";
|
||||||
final String credentials = "controllerId";
|
final String credentials = "controllerId";
|
||||||
final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(principal,
|
final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(
|
||||||
credentials);
|
principal, Lists.newArrayList(credentials));
|
||||||
token.setDetails(webAuthenticationDetailsMock);
|
token.setDetails(webAuthenticationDetailsMock);
|
||||||
|
|
||||||
when(webAuthenticationDetailsMock.getRemoteAddress()).thenReturn(REQUEST_SOURCE_IP);
|
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[] trustedIPAddresses = new String[] { "192.168.1.1", "192.168.1.2", "192.168.1.3" };
|
||||||
final String principal = "controllerId";
|
final String principal = "controllerId";
|
||||||
final String credentials = "controllerId";
|
final String credentials = "controllerId";
|
||||||
final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(principal,
|
final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(
|
||||||
credentials);
|
principal, Lists.newArrayList(credentials));
|
||||||
token.setDetails(webAuthenticationDetailsMock);
|
token.setDetails(webAuthenticationDetailsMock);
|
||||||
|
|
||||||
when(webAuthenticationDetailsMock.getRemoteAddress()).thenReturn(REQUEST_SOURCE_IP);
|
when(webAuthenticationDetailsMock.getRemoteAddress()).thenReturn(REQUEST_SOURCE_IP);
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import org.eclipse.hawkbit.mgmt.json.model.MgmtBaseEntity;
|
||||||
|
|
||||||
|
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 Filter Queries 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;
|
||||||
|
|
||||||
|
public Long getFilterId() {
|
||||||
|
return filterId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFilterId(final Long filterId) {
|
||||||
|
this.filterId = filterId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(final String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getQuery() {
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setQuery(final String query) {
|
||||||
|
this.query = query;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getAutoAssignDistributionSet() {
|
||||||
|
return autoAssignDistributionSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAutoAssignDistributionSet(final Long autoAssignDistributionSet) {
|
||||||
|
this.autoAssignDistributionSet = autoAssignDistributionSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(final String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getQuery() {
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setQuery(String query) {
|
||||||
|
this.query = query;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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.MgmtSoftwareModule;
|
||||||
import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.MgmtSoftwareModuleAssigment;
|
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.target.MgmtTarget;
|
||||||
|
import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQuery;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
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_SORTING, required = false) final String sortParam,
|
||||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) final String rsqlParam);
|
@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<PagedList<MgmtTargetFilterQuery>> 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
|
* Handles the POST request of assigning multiple targets to a single
|
||||||
* distribution set.
|
* distribution set.
|
||||||
|
|||||||
@@ -104,6 +104,11 @@ public final class MgmtRestConstants {
|
|||||||
public static final String DISTRIBUTIONSET_TAG_V1_REQUEST_MAPPING = BASE_V1_REQUEST_MAPPING
|
public static final String DISTRIBUTIONSET_TAG_V1_REQUEST_MAPPING = BASE_V1_REQUEST_MAPPING
|
||||||
+ "/distributionsettags";
|
+ "/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.
|
* The tag URL mapping rest resource.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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<MgmtTargetFilterQuery> 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<PagedList<MgmtTargetFilterQuery>> 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<MgmtTargetFilterQuery> 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<MgmtTargetFilterQuery> 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<Void> 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<MgmtDistributionSet> 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<MgmtTargetFilterQuery> 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<Void> deleteAssignedDistributionSet(@PathVariable("filterId") final Long filterId);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -24,6 +24,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.MgmtSoftwareModule;
|
||||||
import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.MgmtSoftwareModuleAssigment;
|
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.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.MgmtDistributionSetRestApi;
|
||||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
||||||
import org.eclipse.hawkbit.repository.DeploymentManagement;
|
import org.eclipse.hawkbit.repository.DeploymentManagement;
|
||||||
@@ -33,12 +34,14 @@ import org.eclipse.hawkbit.repository.EntityFactory;
|
|||||||
import org.eclipse.hawkbit.repository.OffsetBasedPageRequest;
|
import org.eclipse.hawkbit.repository.OffsetBasedPageRequest;
|
||||||
import org.eclipse.hawkbit.repository.SoftwareManagement;
|
import org.eclipse.hawkbit.repository.SoftwareManagement;
|
||||||
import org.eclipse.hawkbit.repository.SystemManagement;
|
import org.eclipse.hawkbit.repository.SystemManagement;
|
||||||
|
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
|
||||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
|
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
|
||||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||||
import org.eclipse.hawkbit.repository.model.Target;
|
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.repository.model.TargetWithActionType;
|
||||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -67,6 +70,9 @@ public class MgmtDistributionSetResource implements MgmtDistributionSetRestApi {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private TargetManagement targetManagement;
|
private TargetManagement targetManagement;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TargetFilterQueryManagement targetFilterQueryManagement;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private DeploymentManagement deployManagament;
|
private DeploymentManagement deployManagament;
|
||||||
|
|
||||||
@@ -224,6 +230,35 @@ public class MgmtDistributionSetResource implements MgmtDistributionSetRestApi {
|
|||||||
HttpStatus.OK);
|
HttpStatus.OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseEntity<PagedList<MgmtTargetFilterQuery>> 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) {
|
||||||
|
DistributionSet distributionSet = findDistributionSetWithExceptionIfNotFound(distributionSetId);
|
||||||
|
|
||||||
|
final int sanitizedOffsetParam = PagingUtility.sanitizeOffsetParam(pagingOffsetParam);
|
||||||
|
final int sanitizedLimitParam = PagingUtility.sanitizePageLimitParam(pagingLimitParam);
|
||||||
|
final Sort sorting = PagingUtility.sanitizeTargetFilterQuerySortParam(sortParam);
|
||||||
|
|
||||||
|
final Pageable pageable = new OffsetBasedPageRequest(sanitizedOffsetParam, sanitizedLimitParam, sorting);
|
||||||
|
final Page<TargetFilterQuery> targetFilterQueries;
|
||||||
|
if (rsqlParam != null) {
|
||||||
|
targetFilterQueries = targetFilterQueryManagement.findTargetFilterQueryByAutoAssignDS(pageable,
|
||||||
|
distributionSet, rsqlParam);
|
||||||
|
} else {
|
||||||
|
targetFilterQueries = targetFilterQueryManagement.findTargetFilterQueryByAutoAssignDS(pageable,
|
||||||
|
distributionSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ResponseEntity<>(
|
||||||
|
new PagedList<>(MgmtTargetFilterQueryMapper.toResponse(targetFilterQueries.getContent()),
|
||||||
|
targetFilterQueries.getTotalElements()),
|
||||||
|
HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ResponseEntity<MgmtTargetAssignmentResponseBody> createAssignedTarget(
|
public ResponseEntity<MgmtTargetAssignmentResponseBody> createAssignedTarget(
|
||||||
@PathVariable("distributionSetId") final Long distributionSetId,
|
@PathVariable("distributionSetId") final Long distributionSetId,
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
/**
|
||||||
|
* 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.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;
|
||||||
|
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
|
||||||
|
* back.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public final class MgmtTargetFilterQueryMapper {
|
||||||
|
|
||||||
|
private MgmtTargetFilterQueryMapper() {
|
||||||
|
// Utility class
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a response for target filter queries.
|
||||||
|
*
|
||||||
|
* @param filters
|
||||||
|
* list of targets
|
||||||
|
* @return the response
|
||||||
|
*/
|
||||||
|
public static List<MgmtTargetFilterQuery> toResponse(final List<TargetFilterQuery> filters) {
|
||||||
|
if (CollectionUtils.isEmpty(filters)) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
return filters.stream().map(MgmtTargetFilterQueryMapper::toResponse).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a response for target filter query.
|
||||||
|
*
|
||||||
|
* @param filter
|
||||||
|
* the target
|
||||||
|
* @return the response
|
||||||
|
*/
|
||||||
|
public static MgmtTargetFilterQuery toResponse(final TargetFilterQuery filter) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,175 @@
|
|||||||
|
/**
|
||||||
|
* 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<MgmtTargetFilterQuery> 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<PagedList<MgmtTargetFilterQuery>> 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.sanitizeTargetFilterQuerySortParam(sortParam);
|
||||||
|
|
||||||
|
final Pageable pageable = new OffsetBasedPageRequest(sanitizedOffsetParam, sanitizedLimitParam, sorting);
|
||||||
|
final Slice<TargetFilterQuery> findTargetFiltersAll;
|
||||||
|
final Long countTargetsAll;
|
||||||
|
if (rsqlParam != null) {
|
||||||
|
final Page<TargetFilterQuery> findFilterPage = filterManagement
|
||||||
|
.findTargetFilterQueryByFilter(pageable, rsqlParam);
|
||||||
|
countTargetsAll = findFilterPage.getTotalElements();
|
||||||
|
findTargetFiltersAll = findFilterPage;
|
||||||
|
} else {
|
||||||
|
findTargetFiltersAll = filterManagement.findAllTargetFilterQuery(pageable);
|
||||||
|
countTargetsAll = filterManagement.countAllTargetFilterQuery();
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<MgmtTargetFilterQuery> rest = MgmtTargetFilterQueryMapper
|
||||||
|
.toResponse(findTargetFiltersAll.getContent());
|
||||||
|
return new ResponseEntity<>(new PagedList<MgmtTargetFilterQuery>(rest, countTargetsAll), HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseEntity<MgmtTargetFilterQuery> createFilter(@RequestBody MgmtTargetFilterQueryRequestBody filter) {
|
||||||
|
final TargetFilterQuery createdTarget = filterManagement
|
||||||
|
.createTargetFilterQuery(MgmtTargetFilterQueryMapper.fromRequest(entityFactory, filter));
|
||||||
|
|
||||||
|
return new ResponseEntity<>(MgmtTargetFilterQueryMapper.toResponse(createdTarget), HttpStatus.CREATED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseEntity<MgmtTargetFilterQuery> 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 = filterManagement.updateTargetFilterQuery(existingFilter);
|
||||||
|
|
||||||
|
return new ResponseEntity<>(MgmtTargetFilterQueryMapper.toResponse(updateFilter), HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseEntity<Void> deleteFilter(@PathVariable("filterId") Long filterId) {
|
||||||
|
final TargetFilterQuery filter = findFilterWithExceptionIfNotFound(filterId);
|
||||||
|
filterManagement.deleteTargetFilterQuery(filter.getId());
|
||||||
|
LOG.debug("{} target filter query deleted, return status {}", filterId, HttpStatus.OK);
|
||||||
|
return new ResponseEntity<>(HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseEntity<MgmtTargetFilterQuery> 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 = filterManagement.updateTargetFilterQuery(filter);
|
||||||
|
|
||||||
|
return new ResponseEntity<>(MgmtTargetFilterQueryMapper.toResponse(updateFilter), HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseEntity<MgmtDistributionSet> getAssignedDistributionSet(@PathVariable("filterId") Long filterId) {
|
||||||
|
final TargetFilterQuery filter = findFilterWithExceptionIfNotFound(filterId);
|
||||||
|
DistributionSet autoAssignDistributionSet = filter.getAutoAssignDistributionSet();
|
||||||
|
MgmtDistributionSet distributionSetRest = MgmtDistributionSetMapper.toResponse(autoAssignDistributionSet);
|
||||||
|
final HttpStatus retStatus = distributionSetRest == null ? HttpStatus.NO_CONTENT : HttpStatus.OK;
|
||||||
|
return new ResponseEntity<>(distributionSetRest, retStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseEntity<Void> deleteAssignedDistributionSet(@PathVariable("filterId") Long filterId) {
|
||||||
|
final TargetFilterQuery filter = findFilterWithExceptionIfNotFound(filterId);
|
||||||
|
|
||||||
|
filter.setAutoAssignDistributionSet(null);
|
||||||
|
|
||||||
|
filterManagement.updateTargetFilterQuery(filter);
|
||||||
|
|
||||||
|
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TargetFilterQuery findFilterWithExceptionIfNotFound(final Long filterId) {
|
||||||
|
final TargetFilterQuery filter = filterManagement.findTargetFilterQueryById(filterId);
|
||||||
|
if (filter == null) {
|
||||||
|
throw new EntityNotFoundException("TargetFilterQuery with Id {" + filterId + "} does not exist");
|
||||||
|
}
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -20,6 +20,7 @@ import org.eclipse.hawkbit.repository.SoftwareModuleFields;
|
|||||||
import org.eclipse.hawkbit.repository.SoftwareModuleMetadataFields;
|
import org.eclipse.hawkbit.repository.SoftwareModuleMetadataFields;
|
||||||
import org.eclipse.hawkbit.repository.SoftwareModuleTypeFields;
|
import org.eclipse.hawkbit.repository.SoftwareModuleTypeFields;
|
||||||
import org.eclipse.hawkbit.repository.TargetFields;
|
import org.eclipse.hawkbit.repository.TargetFields;
|
||||||
|
import org.eclipse.hawkbit.repository.TargetFilterQueryFields;
|
||||||
import org.eclipse.hawkbit.rest.util.SortUtility;
|
import org.eclipse.hawkbit.rest.util.SortUtility;
|
||||||
import org.springframework.data.domain.Sort;
|
import org.springframework.data.domain.Sort;
|
||||||
import org.springframework.data.domain.Sort.Direction;
|
import org.springframework.data.domain.Sort.Direction;
|
||||||
@@ -59,6 +60,14 @@ public final class PagingUtility {
|
|||||||
return new Sort(SortUtility.parse(TargetFields.class, sortParam));
|
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) {
|
static Sort sanitizeSoftwareModuleSortParam(final String sortParam) {
|
||||||
if (sortParam == null) {
|
if (sortParam == null) {
|
||||||
// default
|
// default
|
||||||
|
|||||||
@@ -30,11 +30,8 @@ import java.util.Set;
|
|||||||
import org.apache.commons.lang3.RandomStringUtils;
|
import org.apache.commons.lang3.RandomStringUtils;
|
||||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
||||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
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.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.TestdataFactory;
|
||||||
import org.eclipse.hawkbit.repository.test.util.WithUser;
|
import org.eclipse.hawkbit.repository.test.util.WithUser;
|
||||||
import org.eclipse.hawkbit.rest.AbstractRestIntegrationTest;
|
import org.eclipse.hawkbit.rest.AbstractRestIntegrationTest;
|
||||||
@@ -297,6 +294,84 @@ public class MgmtDistributionSetResourceTest extends AbstractRestIntegrationTest
|
|||||||
.andExpect(jsonPath("$.content[0].controllerId", equalTo(knownTargetId)));
|
.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<DistributionSet> createDistributionSetsAlphabetical = createDistributionSetsAlphabetical(1);
|
||||||
|
final DistributionSet createdDs = createDistributionSetsAlphabetical.iterator().next();
|
||||||
|
|
||||||
|
targetFilterQueryManagement
|
||||||
|
.createTargetFilterQuery(entityFactory.generateTargetFilterQuery(knownFilterName, "x==y", 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")).andExpect(status().isOk()).andExpect(jsonPath("$.size", equalTo(1)))
|
||||||
|
.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<DistributionSet> 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<DistributionSet> 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<DistributionSet> 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
|
@Test
|
||||||
@Description("Ensures that DS in repository are listed with proper paging properties.")
|
@Description("Ensures that DS in repository are listed with proper paging properties.")
|
||||||
public void getDistributionSetsWithoutAddtionalRequestParameters() throws Exception {
|
public void getDistributionSetsWithoutAddtionalRequestParameters() throws Exception {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rollout Management properties.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -104,6 +104,27 @@ public interface DeploymentManagement {
|
|||||||
DistributionSetAssignmentResult assignDistributionSet(@NotNull Long dsID,
|
DistributionSetAssignmentResult assignDistributionSet(@NotNull Long dsID,
|
||||||
@NotEmpty Collection<TargetWithActionType> targets);
|
@NotEmpty Collection<TargetWithActionType> 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<TargetWithActionType> targets,
|
||||||
|
String actionMessage);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* method assigns the {@link DistributionSet} to all {@link Target}s by
|
* method assigns the {@link DistributionSet} to all {@link Target}s by
|
||||||
* their IDs with a specific {@link ActionType} and {@code forcetime}.
|
* their IDs with a specific {@link ActionType} and {@code forcetime}.
|
||||||
@@ -364,9 +385,6 @@ public interface DeploymentManagement {
|
|||||||
* pagination parameter
|
* pagination parameter
|
||||||
* @param action
|
* @param action
|
||||||
* to be filtered on
|
* to be filtered on
|
||||||
* @param withMessages
|
|
||||||
* to <code>true</code> if {@link ActionStatus#getMessages()}
|
|
||||||
* need to be fetched.
|
|
||||||
* @return the corresponding {@link Page} of {@link ActionStatus}
|
* @return the corresponding {@link Page} of {@link ActionStatus}
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
||||||
@@ -463,8 +481,6 @@ public interface DeploymentManagement {
|
|||||||
*
|
*
|
||||||
* @param action
|
* @param action
|
||||||
* to be canceled
|
* to be canceled
|
||||||
* @param target
|
|
||||||
* for which the action needs cancellation
|
|
||||||
*
|
*
|
||||||
* @return generated {@link Action} or <code>null</code> if not active on
|
* @return generated {@link Action} or <code>null</code> if not active on
|
||||||
* {@link Target}.
|
* {@link Target}.
|
||||||
@@ -478,8 +494,6 @@ public interface DeploymentManagement {
|
|||||||
* Updates a {@link Action} and forces the {@link Action} if it's not
|
* Updates a {@link Action} and forces the {@link Action} if it's not
|
||||||
* already forced.
|
* already forced.
|
||||||
*
|
*
|
||||||
* @param targetId
|
|
||||||
* the ID of the target
|
|
||||||
* @param actionId
|
* @param actionId
|
||||||
* the ID of the action
|
* the ID of the action
|
||||||
* @return the updated or the found {@link Action}
|
* @return the updated or the found {@link Action}
|
||||||
|
|||||||
@@ -322,6 +322,30 @@ public interface EntityFactory {
|
|||||||
*/
|
*/
|
||||||
TargetFilterQuery generateTargetFilterQuery();
|
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.
|
* Generates an empty {@link TargetTag} without persisting it.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import javax.validation.constraints.NotNull;
|
|||||||
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
|
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
|
||||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
|
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
|
||||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
|
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
|
||||||
|
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
@@ -70,6 +71,13 @@ public interface TargetFilterQueryManagement {
|
|||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
||||||
Page<TargetFilterQuery> findAllTargetFilterQuery(@NotNull Pageable pageable);
|
Page<TargetFilterQuery> 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}.
|
* Retrieves all target filter query which {@link TargetFilterQuery}.
|
||||||
*
|
*
|
||||||
@@ -77,11 +85,64 @@ public interface TargetFilterQueryManagement {
|
|||||||
* @param pageable
|
* @param pageable
|
||||||
* pagination parameter
|
* pagination parameter
|
||||||
* @param name
|
* @param name
|
||||||
* target filter query name
|
* name filter
|
||||||
* @return the page with the found {@link TargetFilterQuery}
|
* @return the page with the found {@link TargetFilterQuery}
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
||||||
Page<TargetFilterQuery> findTargetFilterQueryByFilters(@NotNull Pageable pageable, String name);
|
Page<TargetFilterQuery> 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<TargetFilterQuery> 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<TargetFilterQuery> 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<TargetFilterQuery> 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<TargetFilterQuery> findTargetFilterQueryWithAutoAssignDS(@NotNull Pageable pageable);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find target filter query by id.
|
* Find target filter query by id.
|
||||||
@@ -114,4 +175,4 @@ public interface TargetFilterQueryManagement {
|
|||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
|
||||||
TargetFilterQuery updateTargetFilterQuery(@NotNull TargetFilterQuery targetFilterQuery);
|
TargetFilterQuery updateTargetFilterQuery(@NotNull TargetFilterQuery targetFilterQuery);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -248,6 +248,37 @@ public interface TargetManagement {
|
|||||||
List<TargetIdName> findAllTargetIdsByTargetFilterQuery(@NotNull Pageable pageRequest,
|
List<TargetIdName> findAllTargetIdsByTargetFilterQuery(@NotNull Pageable pageRequest,
|
||||||
@NotNull TargetFilterQuery targetFilterQuery);
|
@NotNull TargetFilterQuery targetFilterQuery);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds all targets for all the given parameter {@link TargetFilterQuery}
|
||||||
|
* 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<Target> 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 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 countTargetsByTargetFilterQueryAndNonDS(Long distributionSetId, @NotNull TargetFilterQuery targetFilterQuery);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* retrieves {@link Target}s by the assigned {@link DistributionSet} without
|
* retrieves {@link Target}s by the assigned {@link DistributionSet} without
|
||||||
* details, i.e. NO {@link Target#getTags()} and {@link Target#getActions()}
|
* details, i.e. NO {@link Target#getTags()} and {@link Target#getActions()}
|
||||||
|
|||||||
@@ -88,6 +88,11 @@ public interface DistributionSet extends NamedVersionedEntity {
|
|||||||
*/
|
*/
|
||||||
List<Target> getAssignedTargets();
|
List<Target> getAssignedTargets();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the auto assign target filters
|
||||||
|
*/
|
||||||
|
List<TargetFilterQuery> getAutoAssignFilters();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the installedTargets
|
* @return the installedTargets
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -56,4 +56,16 @@ public interface TargetFilterQuery extends TenantAwareBaseEntity {
|
|||||||
*/
|
*/
|
||||||
void setQuery(String query);
|
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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ import org.eclipse.hawkbit.repository.jpa.JpaTargetManagement;
|
|||||||
import org.eclipse.hawkbit.repository.jpa.JpaTenantConfigurationManagement;
|
import org.eclipse.hawkbit.repository.jpa.JpaTenantConfigurationManagement;
|
||||||
import org.eclipse.hawkbit.repository.jpa.JpaTenantStatsManagement;
|
import org.eclipse.hawkbit.repository.jpa.JpaTenantStatsManagement;
|
||||||
import org.eclipse.hawkbit.repository.jpa.aspects.ExceptionMappingAspectHandler;
|
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.configuration.MultiTenantJpaTransactionManager;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.helper.AfterTransactionCommitExecutorHolder;
|
import org.eclipse.hawkbit.repository.jpa.model.helper.AfterTransactionCommitExecutorHolder;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.helper.CacheManagerHolder;
|
import org.eclipse.hawkbit.repository.jpa.model.helper.CacheManagerHolder;
|
||||||
@@ -399,4 +401,45 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
|||||||
return new JpaEntityFactory();
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -179,13 +179,23 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
|||||||
@CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true)
|
@CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true)
|
||||||
public DistributionSetAssignmentResult assignDistributionSet(final Long dsID,
|
public DistributionSetAssignmentResult assignDistributionSet(final Long dsID,
|
||||||
final Collection<TargetWithActionType> targets) {
|
final Collection<TargetWithActionType> 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<TargetWithActionType> targets,
|
||||||
|
final String actionMessage) {
|
||||||
final JpaDistributionSet set = distributoinSetRepository.findOne(dsID);
|
final JpaDistributionSet set = distributoinSetRepository.findOne(dsID);
|
||||||
if (set == null) {
|
if (set == null) {
|
||||||
throw new EntityNotFoundException(
|
throw new EntityNotFoundException(
|
||||||
String.format("no %s with id %d found", DistributionSet.class.getSimpleName(), dsID));
|
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
|
@Override
|
||||||
@@ -200,21 +210,23 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
|||||||
String.format("no %s with id %d found", DistributionSet.class.getSimpleName(), dsID));
|
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
|
* method assigns the {@link DistributionSet} to all {@link Target}s by
|
||||||
* their IDs with a specific {@link ActionType} and {@code forcetime}.
|
* their IDs with a specific {@link ActionType} and {@code forcetime}.
|
||||||
*
|
*
|
||||||
* @param dsID
|
* @param set
|
||||||
* the ID of the distribution set to assign
|
* the ID of the distribution set to assign
|
||||||
* @param targets
|
* @param targetsWithActionType
|
||||||
* a list of all targets and their action type
|
* a list of all targets and their action type
|
||||||
* @param rollout
|
* @param rollout
|
||||||
* the rollout for this assignment
|
* the rollout for this assignment
|
||||||
* @param rolloutGroup
|
* @param rolloutGroup
|
||||||
* the rollout group for this assignment
|
* the rollout group for this assignment
|
||||||
|
* @param actionMessage
|
||||||
|
* an optional message to be written into the action status
|
||||||
* @return the assignment result
|
* @return the assignment result
|
||||||
*
|
*
|
||||||
* @throw IncompleteDistributionSetException if mandatory
|
* @throw IncompleteDistributionSetException if mandatory
|
||||||
@@ -223,7 +235,7 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
|||||||
*/
|
*/
|
||||||
private DistributionSetAssignmentResult assignDistributionSetToTargets(@NotNull final JpaDistributionSet set,
|
private DistributionSetAssignmentResult assignDistributionSetToTargets(@NotNull final JpaDistributionSet set,
|
||||||
final Collection<TargetWithActionType> targetsWithActionType, final JpaRollout rollout,
|
final Collection<TargetWithActionType> targetsWithActionType, final JpaRollout rollout,
|
||||||
final JpaRolloutGroup rolloutGroup) {
|
final JpaRolloutGroup rolloutGroup, final String actionMessage) {
|
||||||
|
|
||||||
if (!set.isComplete()) {
|
if (!set.isComplete()) {
|
||||||
throw new IncompleteDistributionSetException(
|
throw new IncompleteDistributionSetException(
|
||||||
@@ -296,6 +308,9 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
|||||||
actionStatus.setAction(action);
|
actionStatus.setAction(action);
|
||||||
actionStatus.setOccurredAt(action.getCreatedAt());
|
actionStatus.setOccurredAt(action.getCreatedAt());
|
||||||
actionStatus.setStatus(Status.RUNNING);
|
actionStatus.setStatus(Status.RUNNING);
|
||||||
|
if(actionMessage != null) {
|
||||||
|
actionStatus.addMessage(actionMessage);
|
||||||
|
}
|
||||||
actionStatusRepository.save(actionStatus);
|
actionStatusRepository.save(actionStatus);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -350,7 +365,7 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
|||||||
* the Target which has been assigned to a distribution set
|
* the Target which has been assigned to a distribution set
|
||||||
* @param actionId
|
* @param actionId
|
||||||
* the action id of the assignment
|
* the action id of the assignment
|
||||||
* @param softwareModules
|
* @param modules
|
||||||
* the software modules which have been assigned
|
* the software modules which have been assigned
|
||||||
*/
|
*/
|
||||||
private void assignDistributionSetEvent(final JpaTarget target, final Long actionId,
|
private void assignDistributionSetEvent(final JpaTarget target, final Long actionId,
|
||||||
@@ -367,11 +382,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.
|
* cancellations to the controller.
|
||||||
*
|
*
|
||||||
* @param myTarget
|
* @param targetsIds
|
||||||
* to override {@link UpdateAction}s
|
* to override {@link Action}s
|
||||||
*/
|
*/
|
||||||
private Set<Long> overrideObsoleteUpdateActions(final List<Long> targetsIds) {
|
private Set<Long> overrideObsoleteUpdateActions(final List<Long> targetsIds) {
|
||||||
|
|
||||||
@@ -406,7 +421,7 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
|||||||
|
|
||||||
return assignDistributionSetToTargets(set, tIDs.stream()
|
return assignDistributionSetToTargets(set, tIDs.stream()
|
||||||
.map(t -> new TargetWithActionType(t, actionType, forcedTime)).collect(Collectors.toList()), null,
|
.map(t -> new TargetWithActionType(t, actionType, forcedTime)).collect(Collectors.toList()), null,
|
||||||
null);
|
null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -96,6 +96,9 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private DistributionSetMetadataRepository distributionSetMetadataRepository;
|
private DistributionSetMetadataRepository distributionSetMetadataRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TargetFilterQueryRepository targetFilterQueryRepository;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ActionRepository actionRepository;
|
private ActionRepository actionRepository;
|
||||||
|
|
||||||
@@ -182,7 +185,9 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
|
|||||||
|
|
||||||
// soft delete assigned
|
// soft delete assigned
|
||||||
if (!assigned.isEmpty()) {
|
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
|
// mark the rest as hard delete
|
||||||
|
|||||||
@@ -134,6 +134,16 @@ public class JpaEntityFactory implements EntityFactory {
|
|||||||
return new JpaTargetFilterQuery();
|
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
|
@Override
|
||||||
public SoftwareModuleType generateSoftwareModuleType() {
|
public SoftwareModuleType generateSoftwareModuleType() {
|
||||||
return new JpaSoftwareModuleType();
|
return new JpaSoftwareModuleType();
|
||||||
|
|||||||
@@ -56,6 +56,9 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
|
|||||||
@Autowired
|
@Autowired
|
||||||
private TargetRepository targetRepository;
|
private TargetRepository targetRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TargetFilterQueryRepository targetFilterQueryRepository;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ActionRepository actionRepository;
|
private ActionRepository actionRepository;
|
||||||
|
|
||||||
@@ -221,6 +224,7 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
|
|||||||
tenantMetaDataRepository.deleteByTenantIgnoreCase(tenant);
|
tenantMetaDataRepository.deleteByTenantIgnoreCase(tenant);
|
||||||
tenantConfigurationRepository.deleteByTenantIgnoreCase(tenant);
|
tenantConfigurationRepository.deleteByTenantIgnoreCase(tenant);
|
||||||
targetRepository.deleteByTenantIgnoreCase(tenant);
|
targetRepository.deleteByTenantIgnoreCase(tenant);
|
||||||
|
targetFilterQueryRepository.deleteByTenantIgnoreCase(tenant);
|
||||||
actionRepository.deleteByTenantIgnoreCase(tenant);
|
actionRepository.deleteByTenantIgnoreCase(tenant);
|
||||||
rolloutGroupRepository.deleteByTenantIgnoreCase(tenant);
|
rolloutGroupRepository.deleteByTenantIgnoreCase(tenant);
|
||||||
rolloutRepository.deleteByTenantIgnoreCase(tenant);
|
rolloutRepository.deleteByTenantIgnoreCase(tenant);
|
||||||
|
|||||||
@@ -9,9 +9,11 @@
|
|||||||
package org.eclipse.hawkbit.repository.jpa;
|
package org.eclipse.hawkbit.repository.jpa;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.repository.TargetFields;
|
import org.eclipse.hawkbit.repository.TargetFields;
|
||||||
|
import org.eclipse.hawkbit.repository.TargetFilterQueryFields;
|
||||||
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
|
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
|
||||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery;
|
||||||
@@ -19,6 +21,7 @@ import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
|
|||||||
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
|
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
|
||||||
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
|
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
|
||||||
import org.eclipse.hawkbit.repository.jpa.specifications.TargetFilterQuerySpecification;
|
import org.eclipse.hawkbit.repository.jpa.specifications.TargetFilterQuerySpecification;
|
||||||
|
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
@@ -34,6 +37,8 @@ import org.springframework.validation.annotation.Validated;
|
|||||||
|
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JPA implementation of {@link TargetFilterQueryManagement}.
|
* JPA implementation of {@link TargetFilterQueryManagement}.
|
||||||
*
|
*
|
||||||
@@ -71,20 +76,61 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
|
|||||||
return convertPage(targetFilterQueryRepository.findAll(pageable), pageable);
|
return convertPage(targetFilterQueryRepository.findAll(pageable), pageable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long countAllTargetFilterQuery() {
|
||||||
|
return targetFilterQueryRepository.count();
|
||||||
|
}
|
||||||
|
|
||||||
private static Page<TargetFilterQuery> convertPage(final Page<JpaTargetFilterQuery> findAll,
|
private static Page<TargetFilterQuery> convertPage(final Page<JpaTargetFilterQuery> findAll,
|
||||||
final Pageable pageable) {
|
final Pageable pageable) {
|
||||||
return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements());
|
return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Page<TargetFilterQuery> findTargetFilterQueryByFilters(final Pageable pageable, final String name) {
|
public Page<TargetFilterQuery> findTargetFilterQueryByName(final Pageable pageable, final String name) {
|
||||||
final List<Specification<JpaTargetFilterQuery>> specList = new ArrayList<>();
|
List<Specification<JpaTargetFilterQuery>> specList = Collections.emptyList();
|
||||||
if (!Strings.isNullOrEmpty(name)) {
|
if (!Strings.isNullOrEmpty(name)) {
|
||||||
specList.add(TargetFilterQuerySpecification.likeName(name));
|
specList = Collections.singletonList(TargetFilterQuerySpecification.likeName(name));
|
||||||
}
|
}
|
||||||
return convertPage(findTargetFilterQueryByCriteriaAPI(pageable, specList), pageable);
|
return convertPage(findTargetFilterQueryByCriteriaAPI(pageable, specList), pageable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Page<TargetFilterQuery> findTargetFilterQueryByFilter(@NotNull Pageable pageable, String rsqlFilter) {
|
||||||
|
List<Specification<JpaTargetFilterQuery>> specList = Collections.emptyList();
|
||||||
|
if (!Strings.isNullOrEmpty(rsqlFilter)) {
|
||||||
|
specList = Collections.singletonList(
|
||||||
|
RSQLUtility.parse(rsqlFilter, TargetFilterQueryFields.class, virtualPropertyReplacer));
|
||||||
|
}
|
||||||
|
return convertPage(findTargetFilterQueryByCriteriaAPI(pageable, specList), pageable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Page<TargetFilterQuery> findTargetFilterQueryByAutoAssignDS(@NotNull Pageable pageable,
|
||||||
|
DistributionSet distributionSet) {
|
||||||
|
return findTargetFilterQueryByAutoAssignDS(pageable, distributionSet, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Page<TargetFilterQuery> findTargetFilterQueryByAutoAssignDS(@NotNull Pageable pageable,
|
||||||
|
DistributionSet distributionSet, String rsqlFilter) {
|
||||||
|
final List<Specification<JpaTargetFilterQuery>> specList = new ArrayList<>(2);
|
||||||
|
if (distributionSet != null) {
|
||||||
|
specList.add(TargetFilterQuerySpecification.byAutoAssignDS(distributionSet));
|
||||||
|
}
|
||||||
|
if (!Strings.isNullOrEmpty(rsqlFilter)) {
|
||||||
|
specList.add(RSQLUtility.parse(rsqlFilter, TargetFilterQueryFields.class, virtualPropertyReplacer));
|
||||||
|
}
|
||||||
|
return convertPage(findTargetFilterQueryByCriteriaAPI(pageable, specList), pageable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Page<TargetFilterQuery> findTargetFilterQueryWithAutoAssignDS(@NotNull Pageable pageable) {
|
||||||
|
final List<Specification<JpaTargetFilterQuery>> specList = Collections
|
||||||
|
.singletonList(TargetFilterQuerySpecification.withAutoAssignDS());
|
||||||
|
return convertPage(findTargetFilterQueryByCriteriaAPI(pageable, specList), pageable);
|
||||||
|
}
|
||||||
|
|
||||||
private Page<JpaTargetFilterQuery> findTargetFilterQueryByCriteriaAPI(final Pageable pageable,
|
private Page<JpaTargetFilterQuery> findTargetFilterQueryByCriteriaAPI(final Pageable pageable,
|
||||||
final List<Specification<JpaTargetFilterQuery>> specList) {
|
final List<Specification<JpaTargetFilterQuery>> specList) {
|
||||||
if (specList == null || specList.isEmpty()) {
|
if (specList == null || specList.isEmpty()) {
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import javax.persistence.criteria.JoinType;
|
|||||||
import javax.persistence.criteria.Order;
|
import javax.persistence.criteria.Order;
|
||||||
import javax.persistence.criteria.Predicate;
|
import javax.persistence.criteria.Predicate;
|
||||||
import javax.persistence.criteria.Root;
|
import javax.persistence.criteria.Root;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.eclipse.hawkbit.repository.FilterParams;
|
import org.eclipse.hawkbit.repository.FilterParams;
|
||||||
@@ -570,7 +571,8 @@ public class JpaTargetManagement implements TargetManagement {
|
|||||||
final CriteriaQuery<Object[]> multiselect = query.multiselect(targetRoot.get(JpaTarget_.id),
|
final CriteriaQuery<Object[]> multiselect = query.multiselect(targetRoot.get(JpaTarget_.id),
|
||||||
targetRoot.get(JpaTarget_.controllerId), targetRoot.get(JpaTarget_.name), targetRoot.get(sortProperty));
|
targetRoot.get(JpaTarget_.controllerId), targetRoot.get(JpaTarget_.name), targetRoot.get(sortProperty));
|
||||||
|
|
||||||
final Specification<JpaTarget> spec = RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class, virtualPropertyReplacer);
|
final Specification<JpaTarget> spec = RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class,
|
||||||
|
virtualPropertyReplacer);
|
||||||
final Predicate[] specificationsForMultiSelect = specificationsToPredicate(Lists.newArrayList(spec), targetRoot,
|
final Predicate[] specificationsForMultiSelect = specificationsToPredicate(Lists.newArrayList(spec), targetRoot,
|
||||||
multiselect, cb);
|
multiselect, cb);
|
||||||
|
|
||||||
@@ -584,6 +586,33 @@ public class JpaTargetManagement implements TargetManagement {
|
|||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Page<Target> findAllTargetsByTargetFilterQueryAndNonDS(@NotNull Pageable pageRequest, Long distributionSetId,
|
||||||
|
@NotNull TargetFilterQuery targetFilterQuery) {
|
||||||
|
|
||||||
|
final Specification<JpaTarget> spec = RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class,
|
||||||
|
virtualPropertyReplacer);
|
||||||
|
|
||||||
|
return findTargetsBySpec(
|
||||||
|
(root, cq,
|
||||||
|
cb) -> cb.and(spec.toPredicate(root, cq, cb), TargetSpecifications
|
||||||
|
.hasNotDistributionSetInActions(distributionSetId).toPredicate(root, cq, cb)),
|
||||||
|
pageRequest);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long countTargetsByTargetFilterQueryAndNonDS(Long distributionSetId,
|
||||||
|
@NotNull TargetFilterQuery targetFilterQuery) {
|
||||||
|
final Specification<JpaTarget> spec = RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class,
|
||||||
|
virtualPropertyReplacer);
|
||||||
|
final List<Specification<JpaTarget>> specList = new ArrayList<>(2);
|
||||||
|
specList.add(spec);
|
||||||
|
specList.add(TargetSpecifications.hasNotDistributionSetInActions(distributionSetId));
|
||||||
|
|
||||||
|
return countByCriteriaAPI(specList);
|
||||||
|
}
|
||||||
|
|
||||||
@PreDestroy
|
@PreDestroy
|
||||||
void destroy() {
|
void destroy() {
|
||||||
eventBus.unregister(this);
|
eventBus.unregister(this);
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
|||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
import org.springframework.data.jpa.repository.Modifying;
|
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.Isolation;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
@@ -43,4 +45,15 @@ public interface TargetFilterQueryRepository
|
|||||||
@Transactional
|
@Transactional
|
||||||
<S extends JpaTargetFilterQuery> S save(S entity);
|
<S extends JpaTargetFilterQuery> 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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,179 @@
|
|||||||
|
/**
|
||||||
|
* 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.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
public class AutoAssignChecker {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(AutoAssignChecker.class);
|
||||||
|
|
||||||
|
private final TargetFilterQueryManagement targetFilterQueryManagement;
|
||||||
|
|
||||||
|
private final TargetManagement targetManagement;
|
||||||
|
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
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";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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() {
|
||||||
|
|
||||||
|
PageRequest pageRequest = new PageRequest(0, PAGE_SIZE);
|
||||||
|
|
||||||
|
Page<TargetFilterQuery> 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<TargetWithActionType> 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<TargetWithActionType> getTargetsWithActionType(TargetFilterQuery targetFilterQuery, Long dsId,
|
||||||
|
int count) {
|
||||||
|
Page<Target> 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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
/**
|
||||||
|
* 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.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Profile;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scheduler to check target filters for auto assignment of distribution sets
|
||||||
|
*/
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
private final TenantAware tenantAware;
|
||||||
|
|
||||||
|
private final SystemManagement systemManagement;
|
||||||
|
|
||||||
|
private final SystemSecurityContext systemSecurityContext;
|
||||||
|
|
||||||
|
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
|
||||||
|
* 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<String> 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;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -50,6 +50,7 @@ import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
|||||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||||
import org.eclipse.hawkbit.repository.model.Target;
|
import org.eclipse.hawkbit.repository.model.Target;
|
||||||
|
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||||
import org.eclipse.hawkbit.repository.model.TargetInfo;
|
import org.eclipse.hawkbit.repository.model.TargetInfo;
|
||||||
import org.eclipse.persistence.annotations.CascadeOnDelete;
|
import org.eclipse.persistence.annotations.CascadeOnDelete;
|
||||||
import org.eclipse.persistence.descriptors.DescriptorEvent;
|
import org.eclipse.persistence.descriptors.DescriptorEvent;
|
||||||
@@ -95,6 +96,9 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen
|
|||||||
@OneToMany(mappedBy = "assignedDistributionSet", targetEntity = JpaTarget.class, fetch = FetchType.LAZY)
|
@OneToMany(mappedBy = "assignedDistributionSet", targetEntity = JpaTarget.class, fetch = FetchType.LAZY)
|
||||||
private List<Target> assignedToTargets;
|
private List<Target> assignedToTargets;
|
||||||
|
|
||||||
|
@OneToMany(mappedBy = "autoAssignDistributionSet", targetEntity = JpaTargetFilterQuery.class, fetch = FetchType.LAZY)
|
||||||
|
private List<TargetFilterQuery> autoAssignFilters;
|
||||||
|
|
||||||
@OneToMany(mappedBy = "installedDistributionSet", targetEntity = JpaTargetInfo.class, fetch = FetchType.LAZY)
|
@OneToMany(mappedBy = "installedDistributionSet", targetEntity = JpaTargetInfo.class, fetch = FetchType.LAZY)
|
||||||
private List<TargetInfo> installedAtTargets;
|
private List<TargetInfo> installedAtTargets;
|
||||||
|
|
||||||
@@ -224,6 +228,11 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen
|
|||||||
return Collections.unmodifiableList(assignedToTargets);
|
return Collections.unmodifiableList(assignedToTargets);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<TargetFilterQuery> getAutoAssignFilters() {
|
||||||
|
return autoAssignFilters;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<TargetInfo> getInstalledTargets() {
|
public List<TargetInfo> getInstalledTargets() {
|
||||||
if (installedAtTargets == null) {
|
if (installedAtTargets == null) {
|
||||||
|
|||||||
@@ -9,13 +9,19 @@
|
|||||||
package org.eclipse.hawkbit.repository.jpa.model;
|
package org.eclipse.hawkbit.repository.jpa.model;
|
||||||
|
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.ConstraintMode;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.FetchType;
|
||||||
|
import javax.persistence.ForeignKey;
|
||||||
import javax.persistence.Index;
|
import javax.persistence.Index;
|
||||||
|
import javax.persistence.JoinColumn;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
import javax.persistence.Table;
|
import javax.persistence.Table;
|
||||||
import javax.persistence.UniqueConstraint;
|
import javax.persistence.UniqueConstraint;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
import javax.validation.constraints.Size;
|
import javax.validation.constraints.Size;
|
||||||
|
|
||||||
|
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -42,6 +48,10 @@ public class JpaTargetFilterQuery extends AbstractJpaTenantAwareBaseEntity imple
|
|||||||
@NotNull
|
@NotNull
|
||||||
private String query;
|
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() {
|
public JpaTargetFilterQuery() {
|
||||||
// Default constructor for JPA.
|
// Default constructor for JPA.
|
||||||
}
|
}
|
||||||
@@ -59,6 +69,22 @@ public class JpaTargetFilterQuery extends AbstractJpaTenantAwareBaseEntity imple
|
|||||||
this.query = query;
|
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
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
@@ -78,4 +104,14 @@ public class JpaTargetFilterQuery extends AbstractJpaTenantAwareBaseEntity imple
|
|||||||
public void setQuery(final String query) {
|
public void setQuery(final String query) {
|
||||||
this.query = query;
|
this.query = query;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DistributionSet getAutoAssignDistributionSet() {
|
||||||
|
return autoAssignDistributionSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAutoAssignDistributionSet(DistributionSet distributionSet) {
|
||||||
|
autoAssignDistributionSet = (JpaDistributionSet)distributionSet;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ public final class SpecificationsBuilder {
|
|||||||
* where clause.
|
* where clause.
|
||||||
*
|
*
|
||||||
* @param specList
|
* @param specList
|
||||||
* all specification wich will combine
|
* all specification which will combine
|
||||||
* @return <null> if the given specification list is empty
|
* @return <null> if the given specification list is empty
|
||||||
*/
|
*/
|
||||||
public static <T> Specifications<T> combineWithAnd(final List<Specification<T>> specList) {
|
public static <T> Specifications<T> combineWithAnd(final List<Specification<T>> specList) {
|
||||||
@@ -36,8 +36,7 @@ public final class SpecificationsBuilder {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
Specifications<T> specs = Specifications.where(specList.get(0));
|
Specifications<T> specs = Specifications.where(specList.get(0));
|
||||||
specList.remove(0);
|
for (final Specification<T> specification : specList.subList(1, specList.size())) {
|
||||||
for (final Specification<T> specification : specList) {
|
|
||||||
specs = specs.and(specification);
|
specs = specs.and(specification);
|
||||||
}
|
}
|
||||||
return specs;
|
return specs;
|
||||||
|
|||||||
@@ -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.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.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||||
import org.springframework.data.jpa.domain.Specification;
|
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);
|
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<JpaTargetFilterQuery> 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<JpaTargetFilterQuery> withAutoAssignDS() {
|
||||||
|
return (targetFilterQueryRoot, query, cb) -> cb
|
||||||
|
.isNotNull(targetFilterQueryRoot.get(JpaTargetFilterQuery_.autoAssignDistributionSet));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,12 +14,15 @@ import java.util.List;
|
|||||||
import javax.persistence.criteria.CriteriaBuilder;
|
import javax.persistence.criteria.CriteriaBuilder;
|
||||||
import javax.persistence.criteria.Join;
|
import javax.persistence.criteria.Join;
|
||||||
import javax.persistence.criteria.JoinType;
|
import javax.persistence.criteria.JoinType;
|
||||||
|
import javax.persistence.criteria.ListJoin;
|
||||||
import javax.persistence.criteria.Path;
|
import javax.persistence.criteria.Path;
|
||||||
import javax.persistence.criteria.Predicate;
|
import javax.persistence.criteria.Predicate;
|
||||||
import javax.persistence.criteria.Root;
|
import javax.persistence.criteria.Root;
|
||||||
import javax.persistence.criteria.SetJoin;
|
import javax.persistence.criteria.SetJoin;
|
||||||
import javax.validation.constraints.NotNull;
|
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.JpaDistributionSet_;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||||
@@ -234,6 +237,24 @@ public final class TargetSpecifications {
|
|||||||
distributionSetId);
|
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<JpaTarget> hasNotDistributionSetInActions(final Long distributionSetId) {
|
||||||
|
return (targetRoot, query, cb) -> {
|
||||||
|
final ListJoin<JpaTarget, JpaAction> 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
|
* {@link Specification} for retrieving {@link Target}s by assigned
|
||||||
* distribution set.
|
* distribution set.
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -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;
|
||||||
@@ -0,0 +1,257 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
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}.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Features("Component Tests - Repository")
|
||||||
|
@Stories("Target Filter Query Management")
|
||||||
|
public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Test creation of target filter query.")
|
||||||
|
public void createTargetFilterQuery() {
|
||||||
|
final String filterName = "new target filter";
|
||||||
|
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
|
||||||
|
.createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001"));
|
||||||
|
assertEquals("Retrieved newly created custom target filter", targetFilterQuery,
|
||||||
|
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<TargetFilterQuery> 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(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() {
|
||||||
|
final String filterName = "new target filter duplicate";
|
||||||
|
targetFilterQueryManagement
|
||||||
|
.createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001"));
|
||||||
|
|
||||||
|
try {
|
||||||
|
targetFilterQueryManagement
|
||||||
|
.createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001"));
|
||||||
|
fail("should not have worked as query already exists");
|
||||||
|
} catch (final EntityAlreadyExistsException e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Test deletion of target filter query.")
|
||||||
|
public void deleteTargetFilterQuery() {
|
||||||
|
final String filterName = "delete_target_filter_query";
|
||||||
|
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
|
||||||
|
.createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001"));
|
||||||
|
targetFilterQueryManagement.deleteTargetFilterQuery(targetFilterQuery.getId());
|
||||||
|
assertEquals("Returns null as the target filter is deleted", null,
|
||||||
|
targetFilterQueryManagement.findTargetFilterQueryById(targetFilterQuery.getId()));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Test updation of target filter query.")
|
||||||
|
public void updateTargetFilterQuery() {
|
||||||
|
final String filterName = "target_filter_01";
|
||||||
|
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
|
||||||
|
.createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001"));
|
||||||
|
|
||||||
|
final String newQuery = "status==UNKNOWN";
|
||||||
|
targetFilterQuery.setQuery(newQuery);
|
||||||
|
targetFilterQueryManagement.updateTargetFilterQuery(targetFilterQuery);
|
||||||
|
assertEquals("Returns updated target filter query", newQuery,
|
||||||
|
targetFilterQueryManagement.findTargetFilterQueryByName(filterName).getQuery());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@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<TargetFilterQuery> 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<TargetFilterQuery> 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());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,85 +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
|
|
||||||
*/
|
|
||||||
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.JpaTargetFilterQuery;
|
|
||||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import ru.yandex.qatools.allure.annotations.Description;
|
|
||||||
import ru.yandex.qatools.allure.annotations.Features;
|
|
||||||
import ru.yandex.qatools.allure.annotations.Stories;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test class for {@link TargetFilterQueryManagement}.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
@Features("Component Tests - Repository")
|
|
||||||
@Stories("Target Filter Query Management")
|
|
||||||
public class TargetFilterQueryManagenmentTest extends AbstractJpaIntegrationTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@Description("Test creation of target filter query.")
|
|
||||||
public void createTargetFilterQuery() {
|
|
||||||
final String filterName = "new target filter";
|
|
||||||
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
|
|
||||||
.createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001"));
|
|
||||||
assertEquals("Retrieved newly created custom target filter", targetFilterQuery,
|
|
||||||
targetFilterQueryManagement.findTargetFilterQueryByName(filterName));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@Description("Checks if the EntityAlreadyExistsException is thrown if a targetfilterquery with the same name are created more than once.")
|
|
||||||
public void createDuplicateTargetFilterQuery() {
|
|
||||||
final String filterName = "new target filter duplicate";
|
|
||||||
targetFilterQueryManagement
|
|
||||||
.createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001"));
|
|
||||||
|
|
||||||
try {
|
|
||||||
targetFilterQueryManagement
|
|
||||||
.createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001"));
|
|
||||||
fail("should not have worked as query already exists");
|
|
||||||
} catch (final EntityAlreadyExistsException e) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@Description("Test deletion of target filter query.")
|
|
||||||
public void deleteTargetFilterQuery() {
|
|
||||||
final String filterName = "delete_target_filter_query";
|
|
||||||
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
|
|
||||||
.createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001"));
|
|
||||||
targetFilterQueryManagement.deleteTargetFilterQuery(targetFilterQuery.getId());
|
|
||||||
assertEquals("Returns null as the target filter is deleted", null,
|
|
||||||
targetFilterQueryManagement.findTargetFilterQueryById(targetFilterQuery.getId()));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@Description("Test updation of target filter query.")
|
|
||||||
public void updateTargetFilterQuery() {
|
|
||||||
final String filterName = "target_filter_01";
|
|
||||||
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
|
|
||||||
.createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001"));
|
|
||||||
|
|
||||||
final String newQuery = "status==UNKNOWN";
|
|
||||||
targetFilterQuery.setQuery(newQuery);
|
|
||||||
targetFilterQueryManagement.updateTargetFilterQuery(targetFilterQuery);
|
|
||||||
assertEquals("Returns updated target filter query", newQuery,
|
|
||||||
targetFilterQueryManagement.findTargetFilterQueryByName(filterName).getQuery());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -24,15 +24,8 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
|
|||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
|
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.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.junit.Test;
|
||||||
import org.springframework.data.domain.Slice;
|
import org.springframework.data.domain.Slice;
|
||||||
|
|
||||||
@@ -835,6 +828,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<Target> unassignedTargets = targetManagement
|
||||||
|
.createTargets(testdataFactory.generateTargets(12, "unassigned"));
|
||||||
|
List<Target> assignedTargets = targetManagement.createTargets(testdataFactory.generateTargets(10, "assigned"));
|
||||||
|
|
||||||
|
deploymentManagement.assignDistributionSet(assignedSet, assignedTargets);
|
||||||
|
|
||||||
|
List<Target> result = targetManagement.findAllTargetsByTargetFilterQueryAndNonDS(pageReq,
|
||||||
|
assignedSet.getId(), tfq).getContent();
|
||||||
|
assertThat(result)
|
||||||
|
.as("count of targets").hasSize(unassignedTargets.size())
|
||||||
|
.as("contains all targets").containsAll(unassignedTargets);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Verfies that targets with given installed DS are returned from repository.")
|
@Description("Verfies that targets with given installed DS are returned from repository.")
|
||||||
public void findTargetByInstalledDistributionSet() {
|
public void findTargetByInstalledDistributionSet() {
|
||||||
|
|||||||
@@ -0,0 +1,154 @@
|
|||||||
|
/**
|
||||||
|
* 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.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.Target;
|
||||||
|
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
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<Target> 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.countTargetsByTargetFilterQueryAndNonDS(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<Target> targetsF = targetManagement.createTargets(
|
||||||
|
testdataFactory.generateTargets(10, targetDsFIdPref, targetDsFIdPref.concat(" description")));
|
||||||
|
|
||||||
|
List<Target> 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<Target> targets, int count) {
|
||||||
|
List<Long> targetIds = targets.stream().map(Target::getId).collect(Collectors.toList());
|
||||||
|
|
||||||
|
Slice<Target> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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<Specification<Object>> 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<Object> spec = (root, query, cb) -> cb.equal(root.get("field1"), "testValue");
|
||||||
|
List<Specification<Object>> specList = Collections.singletonList(spec);
|
||||||
|
Specifications<Object> 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<Object[]> query = mock(CriteriaQuery.class);
|
||||||
|
final Root<Object> 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<Object> spec1 = (root, query, cb) -> cb.equal(root.get("field1"), "testValue1");
|
||||||
|
Specification<Object> spec2 = (root, query, cb) -> cb.equal(root.get("field2"), "testValue2");
|
||||||
|
|
||||||
|
List<Specification<Object>> specList = new ArrayList<>(2);
|
||||||
|
specList.add(spec1);
|
||||||
|
specList.add(spec2);
|
||||||
|
|
||||||
|
Specifications<Object> 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<Object[]> query = mock(CriteriaQuery.class);
|
||||||
|
final Root<Object> 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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -18,7 +18,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
/**
|
/**
|
||||||
* An abstraction for all controller based security. Check if the tenant
|
* An abstraction for all controller based security. Check if the tenant
|
||||||
* configuration is enabled.
|
* configuration is enabled.
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@@ -46,12 +46,6 @@ public abstract class AbstractControllerAuthenticationFilter implements PreAuthe
|
|||||||
return tenantAware.runAsTenant(secruityToken.getTenant(), configurationKeyTenantRunner);
|
return tenantAware.runAsTenant(secruityToken.getTenant(), configurationKeyTenantRunner);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public abstract HeaderAuthentication getPreAuthenticatedPrincipal(TenantSecurityToken secruityToken);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public abstract HeaderAuthentication getPreAuthenticatedCredentials(TenantSecurityToken secruityToken);
|
|
||||||
|
|
||||||
private final class SecurityConfigurationKeyTenantRunner implements TenantAware.TenantRunner<Boolean> {
|
private final class SecurityConfigurationKeyTenantRunner implements TenantAware.TenantRunner<Boolean> {
|
||||||
@Override
|
@Override
|
||||||
public Boolean run() {
|
public Boolean run() {
|
||||||
|
|||||||
@@ -8,6 +8,10 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.security;
|
package org.eclipse.hawkbit.security;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
|
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
|
||||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||||
@@ -20,8 +24,6 @@ import org.slf4j.LoggerFactory;
|
|||||||
* request URI and the credential from a request header in a the
|
* request URI and the credential from a request header in a the
|
||||||
* {@link TenantSecurityToken}.
|
* {@link TenantSecurityToken}.
|
||||||
*
|
*
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractControllerAuthenticationFilter {
|
public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractControllerAuthenticationFilter {
|
||||||
|
|
||||||
@@ -77,8 +79,7 @@ public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractCont
|
|||||||
@Override
|
@Override
|
||||||
public HeaderAuthentication getPreAuthenticatedPrincipal(final TenantSecurityToken secruityToken) {
|
public HeaderAuthentication getPreAuthenticatedPrincipal(final TenantSecurityToken secruityToken) {
|
||||||
// retrieve the common name header and the authority name header from
|
// retrieve the common name header and the authority name header from
|
||||||
// the http request and
|
// the http request and combine them together
|
||||||
// combine them together
|
|
||||||
final String commonNameValue = secruityToken.getHeader(caCommonNameHeader);
|
final String commonNameValue = secruityToken.getHeader(caCommonNameHeader);
|
||||||
final String knownSslIssuerConfigurationValue = tenantAware.runAsTenant(secruityToken.getTenant(),
|
final String knownSslIssuerConfigurationValue = tenantAware.runAsTenant(secruityToken.getTenant(),
|
||||||
sslIssuerNameConfigTenantRunner);
|
sslIssuerNameConfigTenantRunner);
|
||||||
@@ -97,18 +98,20 @@ public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractCont
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HeaderAuthentication getPreAuthenticatedCredentials(final TenantSecurityToken secruityToken) {
|
public Object getPreAuthenticatedCredentials(final TenantSecurityToken secruityToken) {
|
||||||
final String authorityNameConfigurationValue = tenantAware.runAsTenant(secruityToken.getTenant(),
|
final String authorityNameConfigurationValue = tenantAware.runAsTenant(secruityToken.getTenant(),
|
||||||
sslIssuerNameConfigTenantRunner);
|
sslIssuerNameConfigTenantRunner);
|
||||||
String controllerId = secruityToken.getControllerId();
|
String controllerId = secruityToken.getControllerId();
|
||||||
// in case of legacy download artifact, the controller ID is not in the
|
// in case of legacy download artifact, the controller ID is not in the
|
||||||
// URL path, so then
|
// URL path, so then we just use the common name header
|
||||||
// we just use the common name header
|
|
||||||
if (controllerId == null || "anonymous".equals(controllerId)) {
|
if (controllerId == null || "anonymous".equals(controllerId)) {
|
||||||
controllerId = secruityToken.getHeader(caCommonNameHeader);
|
controllerId = secruityToken.getHeader(caCommonNameHeader);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new HeaderAuthentication(controllerId, authorityNameConfigurationValue);
|
List<String> knownHashes = splitMultiHashBySemicolon(authorityNameConfigurationValue);
|
||||||
|
|
||||||
|
final String cntlId = controllerId;
|
||||||
|
return knownHashes.stream().map(hashItem -> new HeaderAuthentication(cntlId, hashItem)).collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -117,12 +120,15 @@ public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractCont
|
|||||||
* It's ok if we find the the hash in any the trusted CA chain to accept
|
* It's ok if we find the the hash in any the trusted CA chain to accept
|
||||||
* this request for this tenant.
|
* this request for this tenant.
|
||||||
*/
|
*/
|
||||||
private String getIssuerHashHeader(final TenantSecurityToken secruityToken, final String knownIssuerHash) {
|
private String getIssuerHashHeader(final TenantSecurityToken secruityToken, final String knownIssuerHashes) {
|
||||||
|
// there may be several knownIssuerHashes configured for the tenant
|
||||||
|
List<String> knownHashes = splitMultiHashBySemicolon(knownIssuerHashes);
|
||||||
|
|
||||||
// iterate over the headers until we get a null header.
|
// iterate over the headers until we get a null header.
|
||||||
int iHeader = 1;
|
int iHeader = 1;
|
||||||
String foundHash;
|
String foundHash;
|
||||||
while ((foundHash = secruityToken.getHeader(String.format(sslIssuerHashBasicHeader, iHeader))) != null) {
|
while ((foundHash = secruityToken.getHeader(String.format(sslIssuerHashBasicHeader, iHeader))) != null) {
|
||||||
if (foundHash.equals(knownIssuerHash)) {
|
if (knownHashes.contains(foundHash)) {
|
||||||
if (LOGGER.isTraceEnabled()) {
|
if (LOGGER.isTraceEnabled()) {
|
||||||
LOGGER.trace("Found matching ssl issuer hash at position {}", iHeader);
|
LOGGER.trace("Found matching ssl issuer hash at position {}", iHeader);
|
||||||
}
|
}
|
||||||
@@ -148,4 +154,8 @@ public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractCont
|
|||||||
TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_AUTHORITY_NAME, String.class).getValue());
|
TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_AUTHORITY_NAME, String.class).getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static List<String> splitMultiHashBySemicolon(String knownIssuerHashes) {
|
||||||
|
return Arrays.asList(knownIssuerHashes.split(";"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,17 +27,17 @@ import org.springframework.security.web.authentication.preauth.PreAuthenticatedA
|
|||||||
* An spring authentication provider which supports authentication tokens of
|
* An spring authentication provider which supports authentication tokens of
|
||||||
* type {@link PreAuthenticatedAuthenticationToken} created by the
|
* type {@link PreAuthenticatedAuthenticationToken} created by the
|
||||||
* {@link ControllerPreAuthenticatedSecurityHeaderFilter}.
|
* {@link ControllerPreAuthenticatedSecurityHeaderFilter}.
|
||||||
*
|
*
|
||||||
* Additionally to the authentication token providing the principal and the
|
* Additionally to the authentication token providing the principal and the
|
||||||
* credentials which must be match, this authentication provider can also check
|
* credentials which must be match, this authentication provider can also check
|
||||||
* the remote IP address of the request.
|
* the remote IP address of the request.
|
||||||
*
|
*
|
||||||
* E.g. The request path is /controller/v1/{controllerId} then the controllerId
|
* E.g. The request path is /controller/v1/{controllerId} then the controllerId
|
||||||
* in the path is the principal. The credentials are the extracted information
|
* 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
|
* 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
|
* only allowed from a specific source address this authentication manager can
|
||||||
* also check the remote IP address of the request.
|
* also check the remote IP address of the request.
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@@ -58,7 +58,7 @@ public class PreAuthTokenSourceTrustAuthenticationProvider implements Authentica
|
|||||||
* Creates a new PreAuthTokenSourceTrustAuthenticationProvider with given
|
* Creates a new PreAuthTokenSourceTrustAuthenticationProvider with given
|
||||||
* source IP addresses which are trusted and should be checked against the
|
* source IP addresses which are trusted and should be checked against the
|
||||||
* request remote IP address.
|
* request remote IP address.
|
||||||
*
|
*
|
||||||
* @param authorizedSourceIps
|
* @param authorizedSourceIps
|
||||||
* a list of IP addresses.
|
* a list of IP addresses.
|
||||||
*/
|
*/
|
||||||
@@ -70,7 +70,7 @@ public class PreAuthTokenSourceTrustAuthenticationProvider implements Authentica
|
|||||||
* Creates a new PreAuthTokenSourceTrustAuthenticationProvider with given
|
* Creates a new PreAuthTokenSourceTrustAuthenticationProvider with given
|
||||||
* source IP addresses which are trusted and should be checked against the
|
* source IP addresses which are trusted and should be checked against the
|
||||||
* request remote IP address.
|
* request remote IP address.
|
||||||
*
|
*
|
||||||
* @param authorizedSourceIps
|
* @param authorizedSourceIps
|
||||||
* a list of IP addresses.
|
* a list of IP addresses.
|
||||||
*/
|
*/
|
||||||
@@ -87,7 +87,6 @@ public class PreAuthTokenSourceTrustAuthenticationProvider implements Authentica
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean successAuthentication = false;
|
|
||||||
final PreAuthenticatedAuthenticationToken token = (PreAuthenticatedAuthenticationToken) authentication;
|
final PreAuthenticatedAuthenticationToken token = (PreAuthenticatedAuthenticationToken) authentication;
|
||||||
final Object credentials = token.getCredentials();
|
final Object credentials = token.getCredentials();
|
||||||
final Object principal = token.getPrincipal();
|
final Object principal = token.getPrincipal();
|
||||||
@@ -97,14 +96,7 @@ public class PreAuthTokenSourceTrustAuthenticationProvider implements Authentica
|
|||||||
throw new BadCredentialsException("The provided principal and credentials are not match");
|
throw new BadCredentialsException("The provided principal and credentials are not match");
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if principal equals credentials because we want to check if
|
boolean successAuthentication = calculateAuthenticationSuccess(principal, credentials, tokenDetails);
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (successAuthentication) {
|
if (successAuthentication) {
|
||||||
final Collection<GrantedAuthority> controllerAuthorities = new ArrayList<>();
|
final Collection<GrantedAuthority> controllerAuthorities = new ArrayList<>();
|
||||||
@@ -119,6 +111,41 @@ public class PreAuthTokenSourceTrustAuthenticationProvider implements Authentica
|
|||||||
throw new BadCredentialsException("The provided principal and credentials are not match");
|
throw new BadCredentialsException("The provided principal and credentials are not match");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* The credentials may either be of type HeaderAuthentication or of type
|
||||||
|
* Collection<HeaderAuthentication> 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
|
||||||
|
* the {@link HeaderAuthentication} from the header
|
||||||
|
* @param credentials
|
||||||
|
* a single {@link HeaderAuthentication} or a Collection of
|
||||||
|
* HeaderAuthentication
|
||||||
|
* @param tokenDetails
|
||||||
|
* authentication details
|
||||||
|
* @return <code>true</code> if authentication succeeded, otherwise
|
||||||
|
* <code>false</code>
|
||||||
|
*/
|
||||||
|
private boolean calculateAuthenticationSuccess(Object principal, Object credentials, Object tokenDetails) {
|
||||||
|
boolean successAuthentication = false;
|
||||||
|
if (credentials instanceof Collection) {
|
||||||
|
final Collection<?> multiValueCredentials = (Collection<?>) credentials;
|
||||||
|
if (multiValueCredentials.contains(principal)) {
|
||||||
|
successAuthentication = checkSourceIPAddressIfNeccessary(tokenDetails);
|
||||||
|
}
|
||||||
|
} else if (principal.equals(credentials)) {
|
||||||
|
successAuthentication = checkSourceIPAddressIfNeccessary(tokenDetails);
|
||||||
|
}
|
||||||
|
|
||||||
|
return successAuthentication;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean checkSourceIPAddressIfNeccessary(final Object tokenDetails) {
|
private boolean checkSourceIPAddressIfNeccessary(final Object tokenDetails) {
|
||||||
boolean success = authorizedSourceIps == null;
|
boolean success = authorizedSourceIps == null;
|
||||||
String remoteAddress = null;
|
String remoteAddress = null;
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ public interface PreAuthentificationFilter {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the filter is enabled.
|
* Check if the filter is enabled.
|
||||||
*
|
*
|
||||||
* @param secruityToken
|
* @param secruityToken
|
||||||
* the secruity info
|
* the secruity info
|
||||||
* @return <true> is enabled <false> diabled
|
* @return <true> is enabled <false> diabled
|
||||||
@@ -31,7 +31,7 @@ public interface PreAuthentificationFilter {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract the principal information from the current secruityToken.
|
* Extract the principal information from the current secruityToken.
|
||||||
*
|
*
|
||||||
* @param secruityToken
|
* @param secruityToken
|
||||||
* the secruityToken
|
* the secruityToken
|
||||||
* @return the extracted tenant and controller id
|
* @return the extracted tenant and controller id
|
||||||
@@ -40,17 +40,17 @@ public interface PreAuthentificationFilter {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract the principal credentials from the current secruityToken.
|
* Extract the principal credentials from the current secruityToken.
|
||||||
*
|
*
|
||||||
* @param secruityToken
|
* @param secruityToken
|
||||||
* the secruityToken
|
* the secruityToken
|
||||||
* @return the extracted tenant and controller id
|
* @return the extracted tenant and controller id
|
||||||
*/
|
*/
|
||||||
HeaderAuthentication getPreAuthenticatedCredentials(TenantSecurityToken secruityToken);
|
Object getPreAuthenticatedCredentials(TenantSecurityToken secruityToken);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows to add additional authorities to the successful authenticated
|
* Allows to add additional authorities to the successful authenticated
|
||||||
* token.
|
* token.
|
||||||
*
|
*
|
||||||
* @return the authorities granted to the principal, or an empty collection
|
* @return the authorities granted to the principal, or an empty collection
|
||||||
* if the token has not been authenticated. Never null.
|
* if the token has not been authenticated. Never null.
|
||||||
* @see Authentication#getAuthorities()
|
* @see Authentication#getAuthorities()
|
||||||
|
|||||||
@@ -0,0 +1,141 @@
|
|||||||
|
/**
|
||||||
|
* 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.fest.assertions.api.Assertions.assertThat;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
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;
|
||||||
|
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 final 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";
|
||||||
|
|
||||||
|
private static final TenantConfigurationValue<String> CONFIG_VALUE_SINGLE_HASH = TenantConfigurationValue
|
||||||
|
.<String> builder().value(SINGLE_HASH).build();
|
||||||
|
|
||||||
|
private static final TenantConfigurationValue<String> CONFIG_VALUE_MULTI_HASH = TenantConfigurationValue
|
||||||
|
.<String> 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() {
|
||||||
|
final TenantSecurityToken securityToken = prepareSecurityToken(SINGLE_HASH);
|
||||||
|
// use single known hash
|
||||||
|
when(tenantConfigurationManagementMock.getConfigurationValue(
|
||||||
|
eq(TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_AUTHORITY_NAME), eq(String.class)))
|
||||||
|
.thenReturn(CONFIG_VALUE_SINGLE_HASH);
|
||||||
|
assertThat(underTest.getPreAuthenticatedPrincipal(securityToken)).isNotNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Tests the filter for issuer hash based authentication with multiple known hashes")
|
||||||
|
public void testIssuerHashBasedAuthenticationWithMultipleKnownHashes() {
|
||||||
|
final TenantSecurityToken securityToken = prepareSecurityToken(SINGLE_HASH);
|
||||||
|
// use multiple known hashes
|
||||||
|
when(tenantConfigurationManagementMock.getConfigurationValue(
|
||||||
|
eq(TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_AUTHORITY_NAME), eq(String.class)))
|
||||||
|
.thenReturn(CONFIG_VALUE_MULTI_HASH);
|
||||||
|
assertThat(underTest.getPreAuthenticatedPrincipal(securityToken)).isNotNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Tests the filter for issuer hash based authentication with unknown hash")
|
||||||
|
public void testIssuerHashBasedAuthenticationWithUnknownHash() {
|
||||||
|
final TenantSecurityToken securityToken = prepareSecurityToken(UNKNOWN_HASH);
|
||||||
|
// use single known hash
|
||||||
|
when(tenantConfigurationManagementMock.getConfigurationValue(
|
||||||
|
eq(TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_AUTHORITY_NAME), eq(String.class)))
|
||||||
|
.thenReturn(CONFIG_VALUE_MULTI_HASH);
|
||||||
|
assertThat(underTest.getPreAuthenticatedPrincipal(securityToken)).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
final Collection<HeaderAuthentication> credentials1 = (Collection<HeaderAuthentication>) underTest
|
||||||
|
.getPreAuthenticatedCredentials(securityToken1);
|
||||||
|
final Collection<HeaderAuthentication> credentials2 = (Collection<HeaderAuthentication>) underTest
|
||||||
|
.getPreAuthenticatedCredentials(securityToken2);
|
||||||
|
|
||||||
|
final Object principal1 = underTest.getPreAuthenticatedPrincipal(securityToken1);
|
||||||
|
final Object principal2 = underTest.getPreAuthenticatedPrincipal(securityToken2);
|
||||||
|
|
||||||
|
assertThat(credentials1.contains(expected1)).isTrue();
|
||||||
|
assertThat(credentials2.contains(expected2)).isTrue();
|
||||||
|
|
||||||
|
assertEquals("hash1 expected in principal!", expected1, principal1);
|
||||||
|
assertEquals("hash2 expected in principal!", expected2, principal2);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TenantSecurityToken prepareSecurityToken(final String issuerHashHeaderValue) {
|
||||||
|
final TenantSecurityToken securityToken = new TenantSecurityToken("DEFAULT", CA_COMMON_NAME_VALUE,
|
||||||
|
FileResource.createFileResourceBySha1("12345"));
|
||||||
|
securityToken.putHeader(CA_COMMON_NAME, CA_COMMON_NAME_VALUE);
|
||||||
|
securityToken.putHeader(X_SSL_ISSUER_HASH_1, issuerHashHeaderValue);
|
||||||
|
return securityToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -8,9 +8,7 @@
|
|||||||
http://www.eclipse.org/legal/epl-v10.html
|
http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
|
||||||
-->
|
-->
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.eclipse.hawkbit</groupId>
|
<groupId>org.eclipse.hawkbit</groupId>
|
||||||
@@ -22,60 +20,63 @@
|
|||||||
|
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
<build>
|
<profiles>
|
||||||
<plugins>
|
<profile>
|
||||||
<plugin>
|
<id>generateTestReport</id>
|
||||||
<artifactId>maven-resources-plugin</artifactId>
|
<build>
|
||||||
<version>2.7</version>
|
<plugins>
|
||||||
<executions>
|
<plugin>
|
||||||
<execution>
|
<artifactId>maven-resources-plugin</artifactId>
|
||||||
<id>copy-resources</id>
|
<executions>
|
||||||
<phase>verify</phase>
|
<execution>
|
||||||
<goals>
|
<id>copy-resources</id>
|
||||||
<goal>copy-resources</goal>
|
<phase>verify</phase>
|
||||||
</goals>
|
<goals>
|
||||||
|
<goal>copy-resources</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<outputDirectory>${basedir}/target/allure-results</outputDirectory>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>${basedir}/..</directory>
|
||||||
|
<filtering>false</filtering>
|
||||||
|
<includes>
|
||||||
|
<include>**/target/allure-results/*.xml</include>
|
||||||
|
</includes>
|
||||||
|
</resource>
|
||||||
|
<resource>
|
||||||
|
<directory>${basedir}</directory>
|
||||||
|
<filtering>false</filtering>
|
||||||
|
<includes>
|
||||||
|
<include>placeholder.txt</include>
|
||||||
|
</includes>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
<configuration>
|
<configuration>
|
||||||
<outputDirectory>${basedir}/target/allure-results</outputDirectory>
|
<descriptors>
|
||||||
<resources>
|
<descriptor>src/main/assembly/test-report.xml</descriptor>
|
||||||
<resource>
|
</descriptors>
|
||||||
<directory>${basedir}/..</directory>
|
<appendAssemblyId>false</appendAssemblyId>
|
||||||
<filtering>false</filtering>
|
|
||||||
<includes>
|
|
||||||
<include>**/target/allure-results/*.xml</include>
|
|
||||||
</includes>
|
|
||||||
</resource>
|
|
||||||
<resource>
|
|
||||||
<directory>${basedir}</directory>
|
|
||||||
<filtering>false</filtering>
|
|
||||||
<includes>
|
|
||||||
<include>placeholder.txt</include>
|
|
||||||
</includes>
|
|
||||||
</resource>
|
|
||||||
</resources>
|
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
<executions>
|
||||||
</executions>
|
<execution>
|
||||||
</plugin>
|
<id>create-report-zip-assembly</id>
|
||||||
<plugin>
|
<phase>verify</phase>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<goals>
|
||||||
<artifactId>maven-assembly-plugin</artifactId>
|
<goal>single</goal>
|
||||||
<version>2.5.3</version>
|
</goals>
|
||||||
<configuration>
|
</execution>
|
||||||
<descriptors>
|
</executions>
|
||||||
<descriptor>src/main/assembly/test-report.xml</descriptor>
|
</plugin>
|
||||||
</descriptors>
|
</plugins>
|
||||||
<appendAssemblyId>false</appendAssemblyId>
|
</build>
|
||||||
</configuration>
|
</profile>
|
||||||
<executions>
|
</profiles>
|
||||||
<execution>
|
|
||||||
<id>create-report-zip-assembly</id>
|
|
||||||
<phase>verify</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>single</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
</project>
|
</project>
|
||||||
@@ -263,9 +263,9 @@ public class CommonDialogWindow extends Window {
|
|||||||
|
|
||||||
if (field instanceof Table) {
|
if (field instanceof Table) {
|
||||||
((Table) field).addItemSetChangeListener(new ChangeListener(field));
|
((Table) field).addItemSetChangeListener(new ChangeListener(field));
|
||||||
} else {
|
|
||||||
field.addValueChangeListener(new ChangeListener(field));
|
|
||||||
}
|
}
|
||||||
|
field.addValueChangeListener(new ChangeListener(field));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 (distributionSet == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Container dataSource = getContainerDataSource();
|
||||||
|
List<TargetFilterQuery> 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"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -10,6 +10,9 @@ package org.eclipse.hawkbit.ui.components;
|
|||||||
|
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||||
import org.eclipse.hawkbit.ui.common.DistributionSetIdName;
|
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}.
|
* Proxy for {@link DistributionSet}.
|
||||||
@@ -39,6 +42,33 @@ public class ProxyDistribution {
|
|||||||
private String version;
|
private String version;
|
||||||
private String description;
|
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
|
* @return the nameVersion
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ public class ProxyTargetFilter {
|
|||||||
private String createdBy;
|
private String createdBy;
|
||||||
private String lastModifiedBy;
|
private String lastModifiedBy;
|
||||||
private String query;
|
private String query;
|
||||||
|
private ProxyDistribution autoAssignDistributionSet;
|
||||||
|
|
||||||
public String getCreatedDate() {
|
public String getCreatedDate() {
|
||||||
return createdDate;
|
return createdDate;
|
||||||
@@ -90,4 +91,11 @@ public class ProxyTargetFilter {
|
|||||||
this.createdBy = createdBy;
|
this.createdBy = createdBy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ProxyDistribution getAutoAssignDistributionSet() {
|
||||||
|
return autoAssignDistributionSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAutoAssignDistributionSet(ProxyDistribution autoAssignDistributionSet) {
|
||||||
|
this.autoAssignDistributionSet = autoAssignDistributionSet;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.AbstractNamedVersionedEntityTableDetailsLayout;
|
||||||
import org.eclipse.hawkbit.ui.common.detailslayout.DistributionSetMetadatadetailslayout;
|
import org.eclipse.hawkbit.ui.common.detailslayout.DistributionSetMetadatadetailslayout;
|
||||||
import org.eclipse.hawkbit.ui.common.detailslayout.SoftwareModuleDetailsTable;
|
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.common.tagdetails.DistributionTagToken;
|
||||||
import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
|
import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
|
||||||
import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleSmallNoBorder;
|
import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleSmallNoBorder;
|
||||||
@@ -91,6 +92,8 @@ public class DistributionSetDetails extends AbstractNamedVersionedEntityTableDet
|
|||||||
|
|
||||||
private DistributionSetMetadatadetailslayout dsMetadataTable;
|
private DistributionSetMetadatadetailslayout dsMetadataTable;
|
||||||
|
|
||||||
|
private TargetFilterQueryDetailsTable tfqDetailsTable;
|
||||||
|
|
||||||
private VerticalLayout tagsLayout;
|
private VerticalLayout tagsLayout;
|
||||||
|
|
||||||
private Map<String, StringBuilder> assignedSWModule;
|
private Map<String, StringBuilder> assignedSWModule;
|
||||||
@@ -103,9 +106,14 @@ public class DistributionSetDetails extends AbstractNamedVersionedEntityTableDet
|
|||||||
softwareModuleTable = new SoftwareModuleDetailsTable();
|
softwareModuleTable = new SoftwareModuleDetailsTable();
|
||||||
softwareModuleTable.init(getI18n(), true, getPermissionChecker(), distributionSetManagement, getEventBus(),
|
softwareModuleTable.init(getI18n(), true, getPermissionChecker(), distributionSetManagement, getEventBus(),
|
||||||
manageDistUIState);
|
manageDistUIState);
|
||||||
|
|
||||||
dsMetadataTable = new DistributionSetMetadatadetailslayout();
|
dsMetadataTable = new DistributionSetMetadatadetailslayout();
|
||||||
dsMetadataTable.init(getI18n(), getPermissionChecker(), distributionSetManagement, dsMetadataPopupLayout,
|
dsMetadataTable.init(getI18n(), getPermissionChecker(), distributionSetManagement, dsMetadataPopupLayout,
|
||||||
entityFactory);
|
entityFactory);
|
||||||
|
|
||||||
|
tfqDetailsTable = new TargetFilterQueryDetailsTable();
|
||||||
|
tfqDetailsTable.init(getI18n());
|
||||||
|
|
||||||
super.init();
|
super.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,6 +128,7 @@ public class DistributionSetDetails extends AbstractNamedVersionedEntityTableDet
|
|||||||
populateModule();
|
populateModule();
|
||||||
populateTags();
|
populateTags();
|
||||||
populateMetadataDetails();
|
populateMetadataDetails();
|
||||||
|
populateTargetFilterQueries();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void populateModule() {
|
private void populateModule() {
|
||||||
@@ -277,6 +286,10 @@ public class DistributionSetDetails extends AbstractNamedVersionedEntityTableDet
|
|||||||
dsMetadataTable.populateDSMetadata(getSelectedBaseEntity());
|
dsMetadataTable.populateDSMetadata(getSelectedBaseEntity());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void populateTargetFilterQueries() {
|
||||||
|
tfqDetailsTable.populateTableByDistributionSet(getSelectedBaseEntity());
|
||||||
|
}
|
||||||
|
|
||||||
private void updateDistributionSetDetailsLayout(final String type, final Boolean isMigrationRequired) {
|
private void updateDistributionSetDetailsLayout(final String type, final Boolean isMigrationRequired) {
|
||||||
final VerticalLayout detailsTabLayout = getDetailsLayout();
|
final VerticalLayout detailsTabLayout = getDetailsLayout();
|
||||||
detailsTabLayout.removeAllComponents();
|
detailsTabLayout.removeAllComponents();
|
||||||
@@ -332,6 +345,7 @@ public class DistributionSetDetails extends AbstractNamedVersionedEntityTableDet
|
|||||||
detailsTab.addTab(createTagsLayout(), getI18n().get("caption.tags.tab"), null);
|
detailsTab.addTab(createTagsLayout(), getI18n().get("caption.tags.tab"), null);
|
||||||
detailsTab.addTab(createLogLayout(), getI18n().get("caption.logs.tab"), null);
|
detailsTab.addTab(createLogLayout(), getI18n().get("caption.logs.tab"), null);
|
||||||
detailsTab.addTab(dsMetadataTable, getI18n().get("caption.metadata"), null);
|
detailsTab.addTab(dsMetadataTable, getI18n().get("caption.metadata"), null);
|
||||||
|
detailsTab.addTab(tfqDetailsTable, getI18n().get("caption.auto.assignment.ds"), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -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;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSetFilter.DistributionSetFilterBuilder;
|
import org.eclipse.hawkbit.repository.model.DistributionSetFilter.DistributionSetFilterBuilder;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
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.components.ProxyDistribution;
|
||||||
import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil;
|
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.SPUIDefinitions;
|
||||||
import org.eclipse.hawkbit.ui.utils.SpringContextHelper;
|
import org.eclipse.hawkbit.ui.utils.SpringContextHelper;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
@@ -43,11 +41,12 @@ public class ManageDistBeanQuery extends AbstractBeanQuery<ProxyDistribution> {
|
|||||||
|
|
||||||
private static final long serialVersionUID = 5176481314404662215L;
|
private static final long serialVersionUID = 5176481314404662215L;
|
||||||
private Sort sort = new Sort(Direction.ASC, "createdAt");
|
private Sort sort = new Sort(Direction.ASC, "createdAt");
|
||||||
private String searchText = null;
|
private String searchText;
|
||||||
private transient DistributionSetManagement distributionSetManagement;
|
private transient DistributionSetManagement distributionSetManagement;
|
||||||
private transient Page<DistributionSet> firstPageDistributionSets = null;
|
private transient Page<DistributionSet> firstPageDistributionSets;
|
||||||
|
|
||||||
private DistributionSetType distributionSetType = null;
|
private DistributionSetType distributionSetType;
|
||||||
|
private Boolean dsComplete;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -65,14 +64,17 @@ public class ManageDistBeanQuery extends AbstractBeanQuery<ProxyDistribution> {
|
|||||||
if (!Strings.isNullOrEmpty(searchText)) {
|
if (!Strings.isNullOrEmpty(searchText)) {
|
||||||
searchText = String.format("%%%s%%", 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
|
distributionSetType = (DistributionSetType) queryConfig
|
||||||
.get(SPUIDefinitions.FILTER_BY_DISTRIBUTION_SET_TYPE);
|
.get(SPUIDefinitions.FILTER_BY_DISTRIBUTION_SET_TYPE);
|
||||||
}
|
}
|
||||||
|
if(queryConfig.get(SPUIDefinitions.FILTER_BY_DS_COMPLETE) != null) {
|
||||||
|
dsComplete = (Boolean)queryConfig.get(SPUIDefinitions.FILTER_BY_DS_COMPLETE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sortStates.length > 0) {
|
if (sortStates.length > 0) {
|
||||||
// Initalize sort
|
// Initialize sort
|
||||||
sort = new Sort(sortStates[0] ? Direction.ASC : Direction.DESC, (String) sortPropertyIds[0]);
|
sort = new Sort(sortStates[0] ? Direction.ASC : Direction.DESC, (String) sortPropertyIds[0]);
|
||||||
// Add sort
|
// Add sort
|
||||||
for (int distId = 1; distId < sortPropertyIds.length; distId++) {
|
for (int distId = 1; distId < sortPropertyIds.length; distId++) {
|
||||||
@@ -96,27 +98,17 @@ public class ManageDistBeanQuery extends AbstractBeanQuery<ProxyDistribution> {
|
|||||||
} else if (Strings.isNullOrEmpty(searchText)) {
|
} else if (Strings.isNullOrEmpty(searchText)) {
|
||||||
// if no search filters available
|
// if no search filters available
|
||||||
distBeans = getDistributionSetManagement().findDistributionSetsByDeletedAndOrCompleted(
|
distBeans = getDistributionSetManagement().findDistributionSetsByDeletedAndOrCompleted(
|
||||||
new OffsetBasedPageRequest(startIndex, count, sort), false, null);
|
new OffsetBasedPageRequest(startIndex, count, sort), false, dsComplete);
|
||||||
} else {
|
} else {
|
||||||
final DistributionSetFilter distributionSetFilter = new DistributionSetFilterBuilder().setIsDeleted(false)
|
final DistributionSetFilter distributionSetFilter = new DistributionSetFilterBuilder().setIsDeleted(false)
|
||||||
|
.setIsComplete(dsComplete)
|
||||||
.setSearchText(searchText).setSelectDSWithNoTag(Boolean.FALSE).setType(distributionSetType).build();
|
.setSearchText(searchText).setSelectDSWithNoTag(Boolean.FALSE).setType(distributionSetType).build();
|
||||||
distBeans = getDistributionSetManagement().findDistributionSetsByFilters(
|
distBeans = getDistributionSetManagement().findDistributionSetsByFilters(
|
||||||
new PageRequest(startIndex / count, count, sort), distributionSetFilter);
|
new PageRequest(startIndex / count, count, sort), distributionSetFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (final DistributionSet distributionSet : distBeans) {
|
for (final DistributionSet distributionSet : distBeans) {
|
||||||
final ProxyDistribution proxyDistribution = new ProxyDistribution();
|
proxyDistributions.add(new ProxyDistribution(distributionSet));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
return proxyDistributions;
|
return proxyDistributions;
|
||||||
}
|
}
|
||||||
@@ -132,9 +124,10 @@ public class ManageDistBeanQuery extends AbstractBeanQuery<ProxyDistribution> {
|
|||||||
if (Strings.isNullOrEmpty(searchText) && null == distributionSetType) {
|
if (Strings.isNullOrEmpty(searchText) && null == distributionSetType) {
|
||||||
// if no search filters available
|
// if no search filters available
|
||||||
firstPageDistributionSets = getDistributionSetManagement().findDistributionSetsByDeletedAndOrCompleted(
|
firstPageDistributionSets = getDistributionSetManagement().findDistributionSetsByDeletedAndOrCompleted(
|
||||||
new PageRequest(0, SPUIDefinitions.PAGE_SIZE, sort), false, null);
|
new PageRequest(0, SPUIDefinitions.PAGE_SIZE, sort), false, dsComplete);
|
||||||
} else {
|
} else {
|
||||||
final DistributionSetFilter distributionSetFilter = new DistributionSetFilterBuilder().setIsDeleted(false)
|
final DistributionSetFilter distributionSetFilter = new DistributionSetFilterBuilder().setIsDeleted(false)
|
||||||
|
.setIsComplete(dsComplete)
|
||||||
.setSearchText(searchText).setSelectDSWithNoTag(Boolean.FALSE).setType(distributionSetType).build();
|
.setSearchText(searchText).setSelectDSWithNoTag(Boolean.FALSE).setType(distributionSetType).build();
|
||||||
firstPageDistributionSets = getDistributionSetManagement().findDistributionSetsByFilters(
|
firstPageDistributionSets = getDistributionSetManagement().findDistributionSetsByFilters(
|
||||||
new PageRequest(0, SPUIDefinitions.PAGE_SIZE, sort), distributionSetFilter);
|
new PageRequest(0, SPUIDefinitions.PAGE_SIZE, sort), distributionSetFilter);
|
||||||
|
|||||||
@@ -0,0 +1,179 @@
|
|||||||
|
/**
|
||||||
|
* 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<String, Object> queryConfiguration = prepareQueryConfigFilters();
|
||||||
|
final BeanQueryFactory<ManageDistBeanQuery> 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<TableColumn> getVisbleColumns() {
|
||||||
|
final List<TableColumn> 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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setColumnProperties() {
|
||||||
|
setVisibleColumns(getVisbleColumns().stream().map(column -> {
|
||||||
|
setColumnHeader(column.getColumnPropertyId(), column.getColumnHeader());
|
||||||
|
setColumnExpandRatio(column.getColumnPropertyId(), column.getExpandRatio());
|
||||||
|
return column.getColumnPropertyId();
|
||||||
|
}).toArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> prepareQueryConfigFilters() {
|
||||||
|
final Map<String, Object> 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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.countTargetsByTargetFilterQueryAndNonDS(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,8 +13,10 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
|
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
|
||||||
|
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||||
import org.eclipse.hawkbit.ui.common.UserDetailsFormatter;
|
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.components.ProxyTargetFilter;
|
||||||
import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil;
|
import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil;
|
||||||
import org.eclipse.hawkbit.ui.utils.SPDateTimeUtil;
|
import org.eclipse.hawkbit.ui.utils.SPDateTimeUtil;
|
||||||
@@ -85,7 +87,7 @@ public class TargetFilterBeanQuery extends AbstractBeanQuery<ProxyTargetFilter>
|
|||||||
targetFilterQuery = getTargetFilterQueryManagement().findAllTargetFilterQuery(
|
targetFilterQuery = getTargetFilterQueryManagement().findAllTargetFilterQuery(
|
||||||
new PageRequest(startIndex / SPUIDefinitions.PAGE_SIZE, SPUIDefinitions.PAGE_SIZE, sort));
|
new PageRequest(startIndex / SPUIDefinitions.PAGE_SIZE, SPUIDefinitions.PAGE_SIZE, sort));
|
||||||
} else {
|
} else {
|
||||||
targetFilterQuery = getTargetFilterQueryManagement().findTargetFilterQueryByFilters(
|
targetFilterQuery = getTargetFilterQueryManagement().findTargetFilterQueryByName(
|
||||||
new PageRequest(startIndex / SPUIDefinitions.PAGE_SIZE, SPUIDefinitions.PAGE_SIZE, sort),
|
new PageRequest(startIndex / SPUIDefinitions.PAGE_SIZE, SPUIDefinitions.PAGE_SIZE, sort),
|
||||||
searchText);
|
searchText);
|
||||||
}
|
}
|
||||||
@@ -98,6 +100,11 @@ public class TargetFilterBeanQuery extends AbstractBeanQuery<ProxyTargetFilter>
|
|||||||
proxyTarFilter.setModifiedDate(SPDateTimeUtil.getFormattedDate(tarFilterQuery.getLastModifiedAt()));
|
proxyTarFilter.setModifiedDate(SPDateTimeUtil.getFormattedDate(tarFilterQuery.getLastModifiedAt()));
|
||||||
proxyTarFilter.setLastModifiedBy(UserDetailsFormatter.loadAndFormatLastModifiedBy(tarFilterQuery));
|
proxyTarFilter.setLastModifiedBy(UserDetailsFormatter.loadAndFormatLastModifiedBy(tarFilterQuery));
|
||||||
proxyTarFilter.setQuery(tarFilterQuery.getQuery());
|
proxyTarFilter.setQuery(tarFilterQuery.getQuery());
|
||||||
|
|
||||||
|
final DistributionSet distributionSet = tarFilterQuery.getAutoAssignDistributionSet();
|
||||||
|
if (distributionSet != null) {
|
||||||
|
proxyTarFilter.setAutoAssignDistributionSet(new ProxyDistribution(distributionSet));
|
||||||
|
}
|
||||||
proxyTargetFilter.add(proxyTarFilter);
|
proxyTargetFilter.add(proxyTarFilter);
|
||||||
}
|
}
|
||||||
return proxyTargetFilter;
|
return proxyTargetFilter;
|
||||||
@@ -117,7 +124,7 @@ public class TargetFilterBeanQuery extends AbstractBeanQuery<ProxyTargetFilter>
|
|||||||
.findAllTargetFilterQuery(new PageRequest(0, SPUIDefinitions.PAGE_SIZE, sort));
|
.findAllTargetFilterQuery(new PageRequest(0, SPUIDefinitions.PAGE_SIZE, sort));
|
||||||
} else {
|
} else {
|
||||||
firstPageTargetFilter = getTargetFilterQueryManagement()
|
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();
|
final long size = firstPageTargetFilter.getTotalElements();
|
||||||
|
|
||||||
|
|||||||
@@ -10,16 +10,21 @@ package org.eclipse.hawkbit.ui.filtermanagement;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
import javax.annotation.PreDestroy;
|
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.TargetFilterQueryManagement;
|
||||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||||
import org.eclipse.hawkbit.ui.common.ConfirmationDialog;
|
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.components.SPUIComponentProvider;
|
||||||
import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleSmallNoBorder;
|
import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleSmallNoBorder;
|
||||||
import org.eclipse.hawkbit.ui.filtermanagement.event.CustomFilterUIEvent;
|
import org.eclipse.hawkbit.ui.filtermanagement.event.CustomFilterUIEvent;
|
||||||
@@ -44,15 +49,11 @@ import com.vaadin.data.Item;
|
|||||||
import com.vaadin.server.FontAwesome;
|
import com.vaadin.server.FontAwesome;
|
||||||
import com.vaadin.spring.annotation.SpringComponent;
|
import com.vaadin.spring.annotation.SpringComponent;
|
||||||
import com.vaadin.spring.annotation.ViewScope;
|
import com.vaadin.spring.annotation.ViewScope;
|
||||||
import com.vaadin.ui.Button;
|
|
||||||
import com.vaadin.ui.Button.ClickEvent;
|
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;
|
import com.vaadin.ui.themes.ValoTheme;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Displays list of target filter queries
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@SpringComponent
|
@SpringComponent
|
||||||
@@ -76,6 +77,12 @@ public class TargetFilterTable extends Table {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private transient TargetFilterQueryManagement targetFilterQueryManagement;
|
private transient TargetFilterQueryManagement targetFilterQueryManagement;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private transient DistributionSetManagement distributionSetManagement;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private DistributionSetSelectWindow dsSelectWindow;
|
||||||
|
|
||||||
private Container container;
|
private Container container;
|
||||||
|
|
||||||
private static final int PROPERTY_DEPT = 3;
|
private static final int PROPERTY_DEPT = 3;
|
||||||
@@ -142,15 +149,17 @@ public class TargetFilterTable extends Table {
|
|||||||
container.addContainerProperty(SPUILabelDefinitions.VAR_CREATED_DATE, Date.class, null);
|
container.addContainerProperty(SPUILabelDefinitions.VAR_CREATED_DATE, Date.class, null);
|
||||||
container.addContainerProperty(SPUILabelDefinitions.VAR_MODIFIED_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.VAR_MODIFIED_BY, String.class, null);
|
||||||
|
container.addContainerProperty(SPUILabelDefinitions.AUTO_ASSIGN_DISTRIBUTION_SET, String.class, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<TableColumn> getVisbleColumns() {
|
private List<TableColumn> getVisbleColumns() {
|
||||||
final List<TableColumn> columnList = new ArrayList<>();
|
final List<TableColumn> columnList = new ArrayList<>(7);
|
||||||
columnList.add(new TableColumn(SPUILabelDefinitions.NAME, i18n.get("header.name"), 0.2F));
|
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_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.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));
|
columnList.add(new TableColumn(SPUIDefinitions.CUSTOM_FILTER_DELETE, i18n.get("header.delete"), 0.1F));
|
||||||
return columnList;
|
return columnList;
|
||||||
|
|
||||||
@@ -172,7 +181,7 @@ public class TargetFilterTable extends Table {
|
|||||||
return deleteIcon;
|
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)
|
return new StringBuilder(UIComponentIdProvider.CUSTOM_FILTER_DELETE_ICON).append('.').append(targetFilterName)
|
||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
@@ -208,6 +217,9 @@ public class TargetFilterTable extends Table {
|
|||||||
addGeneratedColumn(SPUILabelDefinitions.NAME,
|
addGeneratedColumn(SPUILabelDefinitions.NAME,
|
||||||
(source, itemId, columnId) -> customFilterDetailButton((Long) itemId));
|
(source, itemId, columnId) -> customFilterDetailButton((Long) itemId));
|
||||||
|
|
||||||
|
addGeneratedColumn(SPUILabelDefinitions.AUTO_ASSIGN_DISTRIBUTION_SET,
|
||||||
|
(source, itemId, columnId) -> customFilterDistributionSetButton((Long) itemId));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Button customFilterDetailButton(final Long itemId) {
|
private Button customFilterDetailButton(final Long itemId) {
|
||||||
@@ -222,6 +234,34 @@ public class TargetFilterTable extends Table {
|
|||||||
return updateIcon;
|
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) {
|
private void onClickOfDetailButton(final ClickEvent event) {
|
||||||
final String targetFilterName = (String) ((Button) event.getComponent()).getData();
|
final String targetFilterName = (String) ((Button) event.getComponent()).getData();
|
||||||
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
|
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
|
||||||
@@ -246,14 +286,11 @@ public class TargetFilterTable extends Table {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setColumnProperties() {
|
private void setColumnProperties() {
|
||||||
final List<TableColumn> columnList = getVisbleColumns();
|
setVisibleColumns(getVisbleColumns().stream().map(column -> {
|
||||||
final List<Object> swColumnIds = new ArrayList<>();
|
|
||||||
for (final TableColumn column : columnList) {
|
|
||||||
setColumnHeader(column.getColumnPropertyId(), column.getColumnHeader());
|
setColumnHeader(column.getColumnPropertyId(), column.getColumnHeader());
|
||||||
setColumnExpandRatio(column.getColumnPropertyId(), column.getExpandRatio());
|
setColumnExpandRatio(column.getColumnPropertyId(), column.getExpandRatio());
|
||||||
swColumnIds.add(column.getColumnPropertyId());
|
return column.getColumnPropertyId();
|
||||||
}
|
}).toArray());
|
||||||
setVisibleColumns(swColumnIds.toArray());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,13 +63,17 @@ public class CertificateAuthenticationConfigurationItem extends AbstractAuthenti
|
|||||||
final Label caRootAuthorityLabel = new LabelBuilder().name("SSL Issuer Hash:").buildLabel();
|
final Label caRootAuthorityLabel = new LabelBuilder().name("SSL Issuer Hash:").buildLabel();
|
||||||
caRootAuthorityLabel.setDescription(
|
caRootAuthorityLabel.setDescription(
|
||||||
"The SSL Issuer iRules.X509 hash, to validate against the controller request certifcate.");
|
"The SSL Issuer iRules.X509 hash, to validate against the controller request certifcate.");
|
||||||
|
caRootAuthorityLabel.setWidthUndefined();
|
||||||
|
|
||||||
caRootAuthorityTextField = new TextFieldBuilder().immediate(true).maxLengthAllowed(128).buildTextComponent();
|
caRootAuthorityTextField = new TextFieldBuilder().immediate(true).maxLengthAllowed(160).buildTextComponent();
|
||||||
caRootAuthorityTextField.setWidth("500px");
|
caRootAuthorityTextField.setWidth("100%");
|
||||||
caRootAuthorityTextField.addTextChangeListener(event -> caRootAuthorityChanged());
|
caRootAuthorityTextField.addTextChangeListener(event -> caRootAuthorityChanged());
|
||||||
|
|
||||||
caRootAuthorityLayout.addComponent(caRootAuthorityLabel);
|
caRootAuthorityLayout.addComponent(caRootAuthorityLabel);
|
||||||
|
caRootAuthorityLayout.setExpandRatio(caRootAuthorityLabel, 0);
|
||||||
caRootAuthorityLayout.addComponent(caRootAuthorityTextField);
|
caRootAuthorityLayout.addComponent(caRootAuthorityTextField);
|
||||||
|
caRootAuthorityLayout.setExpandRatio(caRootAuthorityTextField, 1);
|
||||||
|
caRootAuthorityLayout.setWidth("100%");
|
||||||
|
|
||||||
detailLayout.addComponent(caRootAuthorityLayout);
|
detailLayout.addComponent(caRootAuthorityLayout);
|
||||||
|
|
||||||
|
|||||||
@@ -487,6 +487,11 @@ public final class SPUIDefinitions {
|
|||||||
*/
|
*/
|
||||||
public static final String FILTER_BY_INVALID_QUERY = "FilterByInvalidFilterQueryText";
|
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.
|
* Sort order of column - created at in target table.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -203,6 +203,10 @@ public final class SPUILabelDefinitions {
|
|||||||
* ASSIGNED DISTRIBUTION ID.
|
* ASSIGNED DISTRIBUTION ID.
|
||||||
*/
|
*/
|
||||||
public static final String ASSIGNED_DISTRIBUTION_ID = "assignedDistributionSet.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.
|
* ASSIGNED DISTRIBUTION Name & Version.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -885,6 +885,26 @@ public final class UIComponentIdProvider {
|
|||||||
*/
|
*/
|
||||||
public static final String FILTER_SEARCH_ICON_ID = "filter.search.icon";
|
public static final String FILTER_SEARCH_ICON_ID = "filter.search.icon";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
* /* Private Constructor.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -168,6 +168,7 @@
|
|||||||
|
|
||||||
.v-table-cell-content {
|
.v-table-cell-content {
|
||||||
height: 27px;
|
height: 27px;
|
||||||
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,8 +22,11 @@ button.no.actions = No actions
|
|||||||
button.ok = OK
|
button.ok = OK
|
||||||
button.cancel = Cancel
|
button.cancel = Cancel
|
||||||
button.upload.file = Upload File
|
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.
|
bulk.targets.upload = Please upload csv file.
|
||||||
bulkupload.ds.name = DS Name
|
bulkupload.ds.name = DS Name
|
||||||
|
button.discard=Discard
|
||||||
|
|
||||||
# Headers prefix with - header
|
# Headers prefix with - header
|
||||||
header.target.table=Targets
|
header.target.table=Targets
|
||||||
@@ -48,6 +51,14 @@ header.dist.twintable.selected=Selected
|
|||||||
header.dist.twintable.available=Available
|
header.dist.twintable.available=Available
|
||||||
header.target.installed = Installed
|
header.target.installed = Installed
|
||||||
header.target.assigned = Assigned
|
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
|
# Captions prefix with - caption
|
||||||
caption.action.history = Action history for {0}
|
caption.action.history = Action history for {0}
|
||||||
@@ -59,6 +70,7 @@ caption.filter.simple = Simple Filter
|
|||||||
caption.filter.custom = Custom Filter
|
caption.filter.custom = Custom Filter
|
||||||
caption.metadata = Metadata
|
caption.metadata = Metadata
|
||||||
|
|
||||||
|
caption.select.auto.assign.dist = Select auto assignment distribution set
|
||||||
caption.add.softwaremodule = Configure Software Module
|
caption.add.softwaremodule = Configure Software Module
|
||||||
caption.add.new.dist = Configure New Distribution
|
caption.add.new.dist = Configure New Distribution
|
||||||
caption.update.dist = Configure Update Distribution
|
caption.update.dist = Configure Update Distribution
|
||||||
@@ -92,7 +104,8 @@ caption.confirm.abort.action = Confirm Abort Action
|
|||||||
caption.filter.delete.confirmbox = Confirm Filter Delete Action
|
caption.filter.delete.confirmbox = Confirm Filter Delete Action
|
||||||
caption.metadata.popup = Metadata of
|
caption.metadata.popup = Metadata of
|
||||||
caption.metadata.delete.action.confirmbox = Confirm Metadata Delete Action
|
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
|
# Labels prefix with - label
|
||||||
label.dist.details.type = Type :
|
label.dist.details.type = Type :
|
||||||
@@ -129,8 +142,6 @@ label.combobox.tag = Select Tag
|
|||||||
label.choose.tag = Choose Tag to update
|
label.choose.tag = Choose Tag to update
|
||||||
label.choose.tag.color = Choose Tag Color
|
label.choose.tag.color = Choose Tag Color
|
||||||
label.target.filtered.total = Total filtered targets :
|
label.target.filtered.total = Total filtered targets :
|
||||||
label.filter.selected = Selected:
|
|
||||||
label.filter.shown = Shown:
|
|
||||||
label.filter = Filter :
|
label.filter = Filter :
|
||||||
label.target.filter.count = Total Targets :
|
label.target.filter.count = Total Targets :
|
||||||
label.filter.selected = Selected :
|
label.filter.selected = Selected :
|
||||||
@@ -161,13 +172,15 @@ label.filter.by.status = Filter by Status
|
|||||||
label.filter.by.overdue = Filter by Overdue
|
label.filter.by.overdue = Filter by Overdue
|
||||||
label.target.controller.attrs = <b>Controller attributes</b>
|
label.target.controller.attrs = <b>Controller attributes</b>
|
||||||
label.target.lastpolldate = Last poll :
|
label.target.lastpolldate = Last poll :
|
||||||
label.no.tag.assigned = NO TAG
|
|
||||||
label.tag.name = Tag name
|
label.tag.name = Tag name
|
||||||
label.configuration.auth.header = Allow targets to authenticate via a certificate authenticated by an reverse proxy
|
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.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.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.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.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 label prefix with - checkbox
|
||||||
checkbox.dist.migration.required = Required Migration Step :
|
checkbox.dist.migration.required = Required Migration Step :
|
||||||
@@ -233,7 +246,6 @@ message.dists.already.deleted = Few distribution(s) are already deleted.Pending
|
|||||||
message.target.deleted.pending = Target(s) 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.deleted.pending = Distribution(s) already deleted.Pending for action
|
||||||
message.dist.delete.success = All selected distribution sets are deleted successfully !
|
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.delete.success = All selected targets are deleted successfully !
|
||||||
message.target.discard.success = All targets selected for delete are discarded 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.discard.success = All software modules selected for delete are discarded successfully !
|
||||||
@@ -298,7 +310,6 @@ message.no.targets.assiged.fortag = No targets are assigned to tag {0}
|
|||||||
message.error.missing.tagname = Please select tag name
|
message.error.missing.tagname = Please select tag name
|
||||||
message.type.delete = Please unclick the distribution type {0}, then try to delete
|
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.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.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.confirm = Are you sure that you want to delete custom filter?
|
||||||
message.delete.filter.success = Custom filter {0} deleted Successfully!
|
message.delete.filter.success = Custom filter {0} deleted Successfully!
|
||||||
@@ -309,6 +320,8 @@ message.target.filter.duplicate = {0} already exists, please enter another valu
|
|||||||
message.tag.use.bulk.upload = {0} cannot be deleted .It is in use in targets bulk upload
|
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.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.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 info
|
||||||
action.target.table.selectall = Select all (Ctrl+A)
|
action.target.table.selectall = Select all (Ctrl+A)
|
||||||
@@ -450,12 +463,12 @@ header.assigned.ds = Assigned DS
|
|||||||
header.installed.ds = Installed DS
|
header.installed.ds = Installed DS
|
||||||
header.target.status = Status
|
header.target.status = Status
|
||||||
header.target.tags = Tags
|
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.key = Key
|
||||||
header.value = Value
|
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
|
distribution.details.header = Distribution set
|
||||||
target.details.header = Target
|
target.details.header = Target
|
||||||
@@ -512,3 +525,9 @@ menu.title = Software Provisioning
|
|||||||
|
|
||||||
#Target Filter Management
|
#Target Filter Management
|
||||||
breadcrumb.target.filter.custom.filters = Custom Filters
|
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
|
||||||
|
|
||||||
|
|||||||
@@ -1,506 +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.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.overdue = Overdue,
|
|
||||||
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.filter.by.overdue = Filter by Overdue
|
|
||||||
label.target.controller.attrs = <b>Controller attributes</b>
|
|
||||||
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.status.overdue = Overdue
|
|
||||||
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<br>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 <20>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<EFBFBD>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<75>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
|
|
||||||
@@ -1,502 +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.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.overdue = Overdue,
|
|
||||||
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.filter.by.overdue = Filter by Overdue
|
|
||||||
label.target.controller.attrs = <b>Controller attributes</b>
|
|
||||||
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.status.overdue = Overdue
|
|
||||||
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<br>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
|
|
||||||
2
pom.xml
2
pom.xml
@@ -149,7 +149,7 @@
|
|||||||
<!-- Misc libraries versions - END -->
|
<!-- Misc libraries versions - END -->
|
||||||
|
|
||||||
<!-- Release - START -->
|
<!-- Release - START -->
|
||||||
<release.scm.connection>scm:git:https://github.com/eclipse/hawkbit.git</release.scm.connection>
|
<release.scm.connection>scm:git:git@github.com:eclipse/hawkbit.git</release.scm.connection>
|
||||||
<release.scm.developerConnection>scm:git:https://github.com/eclipse/hawkbit.git</release.scm.developerConnection>
|
<release.scm.developerConnection>scm:git:https://github.com/eclipse/hawkbit.git</release.scm.developerConnection>
|
||||||
<release.scm.url>https://github.com/eclipse/hawkbit.git</release.scm.url>
|
<release.scm.url>https://github.com/eclipse/hawkbit.git</release.scm.url>
|
||||||
<!-- Release - END -->
|
<!-- Release - END -->
|
||||||
|
|||||||
Reference in New Issue
Block a user