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:
Dominik Herbst
2016-10-13 10:46:00 +02:00
70 changed files with 3533 additions and 1305 deletions

4
.gitignore vendored
View File

@@ -38,6 +38,10 @@ local.properties
.settings/
.loadpath
# IDEA
*.iml
.idea
# External tool builders
.externalToolBuilders/

View File

@@ -5,7 +5,7 @@
[hawkBit](https://projects.eclipse.org/projects/iot.hawkbit) is an domain independent back end solution for rolling out software updates to constrained edge devices as well as more powerful controllers and gateways connected to IP based networking infrastructure.
Build: [![Circle CI](https://circleci.com/gh/eclipse/hawkbit.svg?style=shield)](https://circleci.com/gh/eclipse/hawkbit)
[![SonarQuality](https://sonar.eu-gb.mybluemix.net/api/badges/gate?key=org.eclipse.hawkbit:hawkbit-parent)](https://sonar.eu-gb.mybluemix.net)
[![SonarQuality](https://sonar.eu-gb.mybluemix.net/api/badges/gate?key=org.eclipse.hawkbit:hawkbit-parent)](https://sonar.eu-gb.mybluemix.net)[![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.eclipse.hawkbit/hawkbit-parent/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.eclipse.hawkbit/hawkbit-parent)
# Documentation
@@ -78,4 +78,4 @@ $ java -jar ./examples/hawkbit-example-mgmt-simulator/target/hawkbit-example-mgm
* `hawkbit-security-core` : Core security elements.
* `hawkbit-security-integration` : Security integration elements to integrate security into hawkBit.
* `hawkbit-test-report` : Test reports
* `hawkbit-ui` : Vaadin UI.
* `hawkbit-ui` : Vaadin UI.

View File

@@ -8,8 +8,7 @@
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>
<parent>
@@ -22,14 +21,31 @@
<name>hawkBit-example :: Parent</name>
<packaging>pom</packaging>
<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>
<profiles>
<profile>
<id>noExampleApp</id>
<modules>
<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>
<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>
<dependencies>

View File

@@ -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;
}
}

View File

@@ -21,6 +21,8 @@ import org.springframework.security.authentication.InsufficientAuthenticationExc
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import com.google.common.collect.Lists;
import ru.yandex.qatools.allure.annotations.Description;
import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Stories;
@@ -45,8 +47,8 @@ public class PreAuthTokenSourceTrustAuthenticationProviderTest {
public void principalAndCredentialsNotTheSameThrowsAuthenticationException() {
final String principal = "controllerIdURL";
final String credentials = "controllerIdHeader";
final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(principal,
credentials);
final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(
principal, Lists.newArrayList(credentials));
token.setDetails(webAuthenticationDetailsMock);
// test, should throw authentication exception
@@ -64,11 +66,12 @@ public class PreAuthTokenSourceTrustAuthenticationProviderTest {
public void principalAndCredentialsAreTheSameWithNoSourceIpCheckIsSuccessful() {
final String principal = "controllerId";
final String credentials = "controllerId";
final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(principal,
credentials);
final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(
principal, Lists.newArrayList(credentials));
token.setDetails(webAuthenticationDetailsMock);
final Authentication authenticate = underTestWithoutSourceIpCheck.authenticate(token);
final Authentication authenticate = underTestWithoutSourceIpCheck
.authenticate(token);
assertThat(authenticate.isAuthenticated()).isTrue();
}
@@ -78,8 +81,8 @@ public class PreAuthTokenSourceTrustAuthenticationProviderTest {
final String remoteAddress = "192.168.1.1";
final String principal = "controllerId";
final String credentials = "controllerId";
final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(principal,
credentials);
final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(
principal, Lists.newArrayList(credentials));
token.setDetails(webAuthenticationDetailsMock);
when(webAuthenticationDetailsMock.getRemoteAddress()).thenReturn(remoteAddress);
@@ -99,14 +102,15 @@ public class PreAuthTokenSourceTrustAuthenticationProviderTest {
public void priniciapAndCredentialsAreTheSameAndSourceIpIsTrusted() {
final String principal = "controllerId";
final String credentials = "controllerId";
final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(principal,
credentials);
final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(
principal, Lists.newArrayList(credentials));
token.setDetails(webAuthenticationDetailsMock);
when(webAuthenticationDetailsMock.getRemoteAddress()).thenReturn(REQUEST_SOURCE_IP);
// test, should throw authentication exception
final Authentication authenticate = underTestWithSourceIpCheck.authenticate(token);
final Authentication authenticate = underTestWithSourceIpCheck
.authenticate(token);
assertThat(authenticate.isAuthenticated()).isTrue();
}
@@ -116,8 +120,8 @@ public class PreAuthTokenSourceTrustAuthenticationProviderTest {
"192.168.1.3" };
final String principal = "controllerId";
final String credentials = "controllerId";
final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(principal,
credentials);
final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(
principal, Lists.newArrayList(credentials));
token.setDetails(webAuthenticationDetailsMock);
when(webAuthenticationDetailsMock.getRemoteAddress()).thenReturn(REQUEST_SOURCE_IP);
@@ -135,8 +139,8 @@ public class PreAuthTokenSourceTrustAuthenticationProviderTest {
final String[] trustedIPAddresses = new String[] { "192.168.1.1", "192.168.1.2", "192.168.1.3" };
final String principal = "controllerId";
final String credentials = "controllerId";
final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(principal,
credentials);
final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(
principal, Lists.newArrayList(credentials));
token.setDetails(webAuthenticationDetailsMock);
when(webAuthenticationDetailsMock.getRemoteAddress()).thenReturn(REQUEST_SOURCE_IP);

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -20,6 +20,7 @@ import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtTargetAssignmentR
import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.MgmtSoftwareModule;
import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.MgmtSoftwareModuleAssigment;
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTarget;
import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQuery;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
@@ -179,6 +180,37 @@ public interface MgmtDistributionSetRestApi {
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) final String sortParam,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) final String rsqlParam);
/**
* Handles the GET request to retrieve target filter queries that have the
* given distribution set as auto assign DS.
*
* @param distributionSetId
* the ID of the distribution set to retrieve the assigned
* targets
* @param pagingOffsetParam
* the offset of list of targets for pagination, might not be
* present in the rest request then default value will be applied
* @param pagingLimitParam
* the limit of the paged request, might not be present in the
* rest request then default value will be applied
* @param sortParam
* the sorting parameter in the request URL, syntax
* {@code field:direction, field:direction}
* @param rsqlParam
* the search name parameter in the request URL, syntax
* {@code q=myFilter}
* @return status OK if get request is successful with the paged list of
* targets
*/
@RequestMapping(method = RequestMethod.GET, value = "/{distributionSetId}/autoAssignTargetFilters", produces = {
MediaType.APPLICATION_JSON_VALUE, "application/hal+json" })
ResponseEntity<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
* distribution set.

View File

@@ -104,6 +104,11 @@ public final class MgmtRestConstants {
public static final String DISTRIBUTIONSET_TAG_V1_REQUEST_MAPPING = BASE_V1_REQUEST_MAPPING
+ "/distributionsettags";
/**
* The target URL mapping rest resource.
*/
public static final String TARGET_FILTER_V1_REQUEST_MAPPING = BASE_V1_REQUEST_MAPPING + "/targetfilters";
/**
* The tag URL mapping rest resource.
*/

View File

@@ -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);
}

View File

@@ -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.MgmtSoftwareModuleAssigment;
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTarget;
import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQuery;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtDistributionSetRestApi;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.repository.DeploymentManagement;
@@ -33,12 +34,14 @@ import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.OffsetBasedPageRequest;
import org.eclipse.hawkbit.repository.SoftwareManagement;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.repository.model.TargetWithActionType;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.slf4j.Logger;
@@ -67,6 +70,9 @@ public class MgmtDistributionSetResource implements MgmtDistributionSetRestApi {
@Autowired
private TargetManagement targetManagement;
@Autowired
private TargetFilterQueryManagement targetFilterQueryManagement;
@Autowired
private DeploymentManagement deployManagament;
@@ -224,6 +230,35 @@ public class MgmtDistributionSetResource implements MgmtDistributionSetRestApi {
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
public ResponseEntity<MgmtTargetAssignmentResponseBody> createAssignedTarget(
@PathVariable("distributionSetId") final Long distributionSetId,

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -20,6 +20,7 @@ import org.eclipse.hawkbit.repository.SoftwareModuleFields;
import org.eclipse.hawkbit.repository.SoftwareModuleMetadataFields;
import org.eclipse.hawkbit.repository.SoftwareModuleTypeFields;
import org.eclipse.hawkbit.repository.TargetFields;
import org.eclipse.hawkbit.repository.TargetFilterQueryFields;
import org.eclipse.hawkbit.rest.util.SortUtility;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
@@ -59,6 +60,14 @@ public final class PagingUtility {
return new Sort(SortUtility.parse(TargetFields.class, sortParam));
}
static Sort sanitizeTargetFilterQuerySortParam(final String sortParam) {
if (sortParam == null) {
// default
return new Sort(Direction.ASC, TargetFilterQueryFields.NAME.getFieldName());
}
return new Sort(SortUtility.parse(TargetFilterQueryFields.class, sortParam));
}
static Sort sanitizeSoftwareModuleSortParam(final String sortParam) {
if (sortParam == null) {
// default

View File

@@ -30,11 +30,8 @@ import java.util.Set;
import org.apache.commons.lang3.RandomStringUtils;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.model.*;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.test.util.TestdataFactory;
import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.eclipse.hawkbit.rest.AbstractRestIntegrationTest;
@@ -297,6 +294,84 @@ public class MgmtDistributionSetResourceTest extends AbstractRestIntegrationTest
.andExpect(jsonPath("$.content[0].controllerId", equalTo(knownTargetId)));
}
@Test
@Description("Ensures that target filters with auto assign DS are returned as persisted in the repository.")
public void getAutoAssignTargetFiltersOfDistributionSet() throws Exception {
// prepare distribution set
final String knownFilterName = "a";
final Set<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
@Description("Ensures that DS in repository are listed with proper paging properties.")
public void getDistributionSetsWithoutAddtionalRequestParameters() throws Exception {

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -104,6 +104,27 @@ public interface DeploymentManagement {
DistributionSetAssignmentResult assignDistributionSet(@NotNull Long dsID,
@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
* their IDs with a specific {@link ActionType} and {@code forcetime}.
@@ -364,9 +385,6 @@ public interface DeploymentManagement {
* pagination parameter
* @param action
* to be filtered on
* @param withMessages
* to <code>true</code> if {@link ActionStatus#getMessages()}
* need to be fetched.
* @return the corresponding {@link Page} of {@link ActionStatus}
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
@@ -463,8 +481,6 @@ public interface DeploymentManagement {
*
* @param action
* to be canceled
* @param target
* for which the action needs cancellation
*
* @return generated {@link Action} or <code>null</code> if not active on
* {@link Target}.
@@ -478,8 +494,6 @@ public interface DeploymentManagement {
* Updates a {@link Action} and forces the {@link Action} if it's not
* already forced.
*
* @param targetId
* the ID of the target
* @param actionId
* the ID of the action
* @return the updated or the found {@link Action}

View File

@@ -322,6 +322,30 @@ public interface EntityFactory {
*/
TargetFilterQuery generateTargetFilterQuery();
/**
* Generates an {@link TargetFilterQuery} without persisting it.
*
* @param name
* name for the filter
* @param query
* query of the filter
* @return {@link TargetFilterQuery} object
*/
TargetFilterQuery generateTargetFilterQuery(String name, String query);
/**
* Generates an {@link TargetFilterQuery} without persisting it.
*
* @param name
* name for the filter
* @param query
* query of the filter
* @param autoAssignDS
* auto assign distribution set
* @return {@link TargetFilterQuery} object
*/
TargetFilterQuery generateTargetFilterQuery(String name, String query, DistributionSet autoAssignDS);
/**
* Generates an empty {@link TargetTag} without persisting it.
*

View File

@@ -13,6 +13,7 @@ import javax.validation.constraints.NotNull;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
@@ -70,6 +71,13 @@ public interface TargetFilterQueryManagement {
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Page<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}.
*
@@ -77,11 +85,64 @@ public interface TargetFilterQueryManagement {
* @param pageable
* pagination parameter
* @param name
* target filter query name
* name filter
* @return the page with the found {@link TargetFilterQuery}
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Page<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.
@@ -114,4 +175,4 @@ public interface TargetFilterQueryManagement {
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
TargetFilterQuery updateTargetFilterQuery(@NotNull TargetFilterQuery targetFilterQuery);
}
}

View File

@@ -248,6 +248,37 @@ public interface TargetManagement {
List<TargetIdName> findAllTargetIdsByTargetFilterQuery(@NotNull Pageable pageRequest,
@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
* details, i.e. NO {@link Target#getTags()} and {@link Target#getActions()}

View File

@@ -88,6 +88,11 @@ public interface DistributionSet extends NamedVersionedEntity {
*/
List<Target> getAssignedTargets();
/**
* @return the auto assign target filters
*/
List<TargetFilterQuery> getAutoAssignFilters();
/**
* @return the installedTargets
*/

View File

@@ -56,4 +56,16 @@ public interface TargetFilterQuery extends TenantAwareBaseEntity {
*/
void setQuery(String query);
/**
* @return the auto assign {@link DistributionSet} if given.
*/
DistributionSet getAutoAssignDistributionSet();
/**
* @param distributionSet
* the {@link DistributionSet} that should be assigned to a
* target when this filter matches.
*/
void setAutoAssignDistributionSet(DistributionSet distributionSet);
}

View File

@@ -42,6 +42,8 @@ import org.eclipse.hawkbit.repository.jpa.JpaTargetManagement;
import org.eclipse.hawkbit.repository.jpa.JpaTenantConfigurationManagement;
import org.eclipse.hawkbit.repository.jpa.JpaTenantStatsManagement;
import org.eclipse.hawkbit.repository.jpa.aspects.ExceptionMappingAspectHandler;
import org.eclipse.hawkbit.repository.jpa.autoassign.AutoAssignChecker;
import org.eclipse.hawkbit.repository.jpa.autoassign.AutoAssignScheduler;
import org.eclipse.hawkbit.repository.jpa.configuration.MultiTenantJpaTransactionManager;
import org.eclipse.hawkbit.repository.jpa.model.helper.AfterTransactionCommitExecutorHolder;
import org.eclipse.hawkbit.repository.jpa.model.helper.CacheManagerHolder;
@@ -399,4 +401,45 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
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);
}
}

View File

@@ -179,13 +179,23 @@ public class JpaDeploymentManagement implements DeploymentManagement {
@CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true)
public DistributionSetAssignmentResult assignDistributionSet(final Long dsID,
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);
if (set == null) {
throw new EntityNotFoundException(
String.format("no %s with id %d found", DistributionSet.class.getSimpleName(), dsID));
}
return assignDistributionSetToTargets(set, targets, null, null);
return assignDistributionSetToTargets(set, targets, null, null, actionMessage);
}
@Override
@@ -200,21 +210,23 @@ public class JpaDeploymentManagement implements DeploymentManagement {
String.format("no %s with id %d found", DistributionSet.class.getSimpleName(), dsID));
}
return assignDistributionSetToTargets(set, targets, (JpaRollout) rollout, (JpaRolloutGroup) rolloutGroup);
return assignDistributionSetToTargets(set, targets, (JpaRollout) rollout, (JpaRolloutGroup) rolloutGroup, null);
}
/**
* method assigns the {@link DistributionSet} to all {@link Target}s by
* their IDs with a specific {@link ActionType} and {@code forcetime}.
*
* @param dsID
* @param set
* the ID of the distribution set to assign
* @param targets
* @param targetsWithActionType
* a list of all targets and their action type
* @param rollout
* the rollout for this assignment
* @param rolloutGroup
* the rollout group for this assignment
* @param actionMessage
* an optional message to be written into the action status
* @return the assignment result
*
* @throw IncompleteDistributionSetException if mandatory
@@ -223,7 +235,7 @@ public class JpaDeploymentManagement implements DeploymentManagement {
*/
private DistributionSetAssignmentResult assignDistributionSetToTargets(@NotNull final JpaDistributionSet set,
final Collection<TargetWithActionType> targetsWithActionType, final JpaRollout rollout,
final JpaRolloutGroup rolloutGroup) {
final JpaRolloutGroup rolloutGroup, final String actionMessage) {
if (!set.isComplete()) {
throw new IncompleteDistributionSetException(
@@ -296,6 +308,9 @@ public class JpaDeploymentManagement implements DeploymentManagement {
actionStatus.setAction(action);
actionStatus.setOccurredAt(action.getCreatedAt());
actionStatus.setStatus(Status.RUNNING);
if(actionMessage != null) {
actionStatus.addMessage(actionMessage);
}
actionStatusRepository.save(actionStatus);
});
@@ -350,7 +365,7 @@ public class JpaDeploymentManagement implements DeploymentManagement {
* the Target which has been assigned to a distribution set
* @param actionId
* the action id of the assignment
* @param softwareModules
* @param modules
* the software modules which have been assigned
*/
private void assignDistributionSetEvent(final JpaTarget target, final Long actionId,
@@ -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.
*
* @param myTarget
* to override {@link UpdateAction}s
* @param targetsIds
* to override {@link Action}s
*/
private Set<Long> overrideObsoleteUpdateActions(final List<Long> targetsIds) {
@@ -406,7 +421,7 @@ public class JpaDeploymentManagement implements DeploymentManagement {
return assignDistributionSetToTargets(set, tIDs.stream()
.map(t -> new TargetWithActionType(t, actionType, forcedTime)).collect(Collectors.toList()), null,
null);
null, null);
}
@Override

View File

@@ -96,6 +96,9 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Autowired
private DistributionSetMetadataRepository distributionSetMetadataRepository;
@Autowired
private TargetFilterQueryRepository targetFilterQueryRepository;
@Autowired
private ActionRepository actionRepository;
@@ -182,7 +185,9 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
// soft delete assigned
if (!assigned.isEmpty()) {
distributionSetRepository.deleteDistributionSet(assigned.toArray(new Long[assigned.size()]));
Long[] dsIds = assigned.toArray(new Long[assigned.size()]);
distributionSetRepository.deleteDistributionSet(dsIds);
targetFilterQueryRepository.unsetAutoAssignDistributionSet(dsIds);
}
// mark the rest as hard delete

View File

@@ -134,6 +134,16 @@ public class JpaEntityFactory implements EntityFactory {
return new JpaTargetFilterQuery();
}
@Override
public TargetFilterQuery generateTargetFilterQuery(String name, String query) {
return new JpaTargetFilterQuery(name, query);
}
@Override
public TargetFilterQuery generateTargetFilterQuery(String name, String query, DistributionSet autoAssignDS) {
return new JpaTargetFilterQuery(name, query, (JpaDistributionSet) autoAssignDS);
}
@Override
public SoftwareModuleType generateSoftwareModuleType() {
return new JpaSoftwareModuleType();

View File

@@ -56,6 +56,9 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
@Autowired
private TargetRepository targetRepository;
@Autowired
private TargetFilterQueryRepository targetFilterQueryRepository;
@Autowired
private ActionRepository actionRepository;
@@ -221,6 +224,7 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
tenantMetaDataRepository.deleteByTenantIgnoreCase(tenant);
tenantConfigurationRepository.deleteByTenantIgnoreCase(tenant);
targetRepository.deleteByTenantIgnoreCase(tenant);
targetFilterQueryRepository.deleteByTenantIgnoreCase(tenant);
actionRepository.deleteByTenantIgnoreCase(tenant);
rolloutGroupRepository.deleteByTenantIgnoreCase(tenant);
rolloutRepository.deleteByTenantIgnoreCase(tenant);

View File

@@ -9,9 +9,11 @@
package org.eclipse.hawkbit.repository.jpa;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.eclipse.hawkbit.repository.TargetFields;
import org.eclipse.hawkbit.repository.TargetFilterQueryFields;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery;
@@ -19,6 +21,7 @@ import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetFilterQuerySpecification;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
@@ -34,6 +37,8 @@ import org.springframework.validation.annotation.Validated;
import com.google.common.base.Strings;
import javax.validation.constraints.NotNull;
/**
* JPA implementation of {@link TargetFilterQueryManagement}.
*
@@ -71,20 +76,61 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
return convertPage(targetFilterQueryRepository.findAll(pageable), pageable);
}
@Override
public Long countAllTargetFilterQuery() {
return targetFilterQueryRepository.count();
}
private static Page<TargetFilterQuery> convertPage(final Page<JpaTargetFilterQuery> findAll,
final Pageable pageable) {
return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements());
}
@Override
public Page<TargetFilterQuery> findTargetFilterQueryByFilters(final Pageable pageable, final String name) {
final List<Specification<JpaTargetFilterQuery>> specList = new ArrayList<>();
public Page<TargetFilterQuery> findTargetFilterQueryByName(final Pageable pageable, final String name) {
List<Specification<JpaTargetFilterQuery>> specList = Collections.emptyList();
if (!Strings.isNullOrEmpty(name)) {
specList.add(TargetFilterQuerySpecification.likeName(name));
specList = Collections.singletonList(TargetFilterQuerySpecification.likeName(name));
}
return convertPage(findTargetFilterQueryByCriteriaAPI(pageable, specList), pageable);
}
@Override
public Page<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,
final List<Specification<JpaTargetFilterQuery>> specList) {
if (specList == null || specList.isEmpty()) {

View File

@@ -26,6 +26,7 @@ import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.validation.constraints.NotNull;
import org.apache.commons.lang3.StringUtils;
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),
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,
multiselect, cb);
@@ -584,6 +586,33 @@ public class JpaTargetManagement implements TargetManagement {
.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
void destroy() {
eventBus.unregister(this);

View File

@@ -13,6 +13,8 @@ import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
@@ -43,4 +45,15 @@ public interface TargetFilterQueryRepository
@Transactional
<S 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);
}

View File

@@ -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());
}
}

View File

@@ -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;
});
}
}

View File

@@ -50,6 +50,7 @@ import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.repository.model.TargetInfo;
import org.eclipse.persistence.annotations.CascadeOnDelete;
import org.eclipse.persistence.descriptors.DescriptorEvent;
@@ -95,6 +96,9 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen
@OneToMany(mappedBy = "assignedDistributionSet", targetEntity = JpaTarget.class, fetch = FetchType.LAZY)
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)
private List<TargetInfo> installedAtTargets;
@@ -224,6 +228,11 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen
return Collections.unmodifiableList(assignedToTargets);
}
@Override
public List<TargetFilterQuery> getAutoAssignFilters() {
return autoAssignFilters;
}
@Override
public List<TargetInfo> getInstalledTargets() {
if (installedAtTargets == null) {

View File

@@ -9,13 +9,19 @@
package org.eclipse.hawkbit.repository.jpa.model;
import javax.persistence.Column;
import javax.persistence.ConstraintMode;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.ForeignKey;
import javax.persistence.Index;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
/**
@@ -42,6 +48,10 @@ public class JpaTargetFilterQuery extends AbstractJpaTenantAwareBaseEntity imple
@NotNull
private String query;
@ManyToOne(optional = true, fetch = FetchType.LAZY, targetEntity = JpaDistributionSet.class)
@JoinColumn(name = "auto_assign_distribution_set", nullable = true, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_filter_auto_assign_ds"))
private JpaDistributionSet autoAssignDistributionSet;
public JpaTargetFilterQuery() {
// Default constructor for JPA.
}
@@ -59,6 +69,22 @@ public class JpaTargetFilterQuery extends AbstractJpaTenantAwareBaseEntity imple
this.query = query;
}
/**
* Construct a Target filter query with auto assign distribution set
*
* @param name
* of the {@link TargetFilterQuery}.
* @param query
* of the {@link TargetFilterQuery}.
* @param autoAssignDistributionSet
* of the {@link TargetFilterQuery}.
*/
public JpaTargetFilterQuery(String name, String query, JpaDistributionSet autoAssignDistributionSet) {
this.name = name;
this.query = query;
this.autoAssignDistributionSet = autoAssignDistributionSet;
}
@Override
public String getName() {
return name;
@@ -78,4 +104,14 @@ public class JpaTargetFilterQuery extends AbstractJpaTenantAwareBaseEntity imple
public void setQuery(final String query) {
this.query = query;
}
@Override
public DistributionSet getAutoAssignDistributionSet() {
return autoAssignDistributionSet;
}
@Override
public void setAutoAssignDistributionSet(DistributionSet distributionSet) {
autoAssignDistributionSet = (JpaDistributionSet)distributionSet;
}
}

View File

@@ -28,7 +28,7 @@ public final class SpecificationsBuilder {
* where clause.
*
* @param specList
* all specification wich will combine
* all specification which will combine
* @return <null> if the given specification list is empty
*/
public static <T> Specifications<T> combineWithAnd(final List<Specification<T>> specList) {
@@ -36,8 +36,7 @@ public final class SpecificationsBuilder {
return null;
}
Specifications<T> specs = Specifications.where(specList.get(0));
specList.remove(0);
for (final Specification<T> specification : specList) {
for (final Specification<T> specification : specList.subList(1, specList.size())) {
specs = specs.and(specification);
}
return specs;

View File

@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.repository.jpa.specifications;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery_;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.springframework.data.jpa.domain.Specification;
@@ -37,4 +38,28 @@ public final class TargetFilterQuerySpecification {
return cb.like(cb.lower(targetFilterQueryRoot.get(JpaTargetFilterQuery_.name)), searchTextToLower);
};
}
/**
* {@link Specification} for retrieving {@link JpaTargetFilterQuery}s based
* on is {@link JpaTargetFilterQuery#getName()}.
*
* @param distributionSet
* of the filter
* @return the {@link JpaTargetFilterQuery} {@link Specification}
*/
public static Specification<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));
}
}

View File

@@ -14,12 +14,15 @@ import java.util.List;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.ListJoin;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.SetJoin;
import javax.validation.constraints.NotNull;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction_;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
@@ -234,6 +237,24 @@ public final class TargetSpecifications {
distributionSetId);
}
/**
* {@link Specification} for retrieving {@link Target}s that don't have the given
* distribution set in their action history
*
* @param distributionSetId
* the ID of the distribution set which must not be assigned
* @return the {@link Target} {@link Specification}
*/
public static Specification<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
* distribution set.

View File

@@ -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;

View File

@@ -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;

View File

@@ -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());
}
}

View File

@@ -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());
}
}

View File

@@ -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.JpaTargetFilterQuery;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.*;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetIdName;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
import org.junit.Test;
import org.springframework.data.domain.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
@Description("Verfies that targets with given installed DS are returned from repository.")
public void findTargetByInstalledDistributionSet() {

View File

@@ -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);
}
}
}
}

View File

@@ -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);
}
}

View File

@@ -18,7 +18,7 @@ import org.slf4j.LoggerFactory;
/**
* An abstraction for all controller based security. Check if the tenant
* configuration is enabled.
*
*
*
*
*/
@@ -46,12 +46,6 @@ public abstract class AbstractControllerAuthenticationFilter implements PreAuthe
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> {
@Override
public Boolean run() {

View File

@@ -8,6 +8,10 @@
*/
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.repository.TenantConfigurationManagement;
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
* {@link TenantSecurityToken}.
*
*
*
*/
public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractControllerAuthenticationFilter {
@@ -77,8 +79,7 @@ public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractCont
@Override
public HeaderAuthentication getPreAuthenticatedPrincipal(final TenantSecurityToken secruityToken) {
// retrieve the common name header and the authority name header from
// the http request and
// combine them together
// the http request and combine them together
final String commonNameValue = secruityToken.getHeader(caCommonNameHeader);
final String knownSslIssuerConfigurationValue = tenantAware.runAsTenant(secruityToken.getTenant(),
sslIssuerNameConfigTenantRunner);
@@ -97,18 +98,20 @@ public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractCont
}
@Override
public HeaderAuthentication getPreAuthenticatedCredentials(final TenantSecurityToken secruityToken) {
public Object getPreAuthenticatedCredentials(final TenantSecurityToken secruityToken) {
final String authorityNameConfigurationValue = tenantAware.runAsTenant(secruityToken.getTenant(),
sslIssuerNameConfigTenantRunner);
String controllerId = secruityToken.getControllerId();
// in case of legacy download artifact, the controller ID is not in the
// URL path, so then
// we just use the common name header
// URL path, so then we just use the common name header
if (controllerId == null || "anonymous".equals(controllerId)) {
controllerId = secruityToken.getHeader(caCommonNameHeader);
}
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
* 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.
int iHeader = 1;
String foundHash;
while ((foundHash = secruityToken.getHeader(String.format(sslIssuerHashBasicHeader, iHeader))) != null) {
if (foundHash.equals(knownIssuerHash)) {
if (knownHashes.contains(foundHash)) {
if (LOGGER.isTraceEnabled()) {
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());
}
}
private static List<String> splitMultiHashBySemicolon(String knownIssuerHashes) {
return Arrays.asList(knownIssuerHashes.split(";"));
}
}

View File

@@ -27,17 +27,17 @@ import org.springframework.security.web.authentication.preauth.PreAuthenticatedA
* An spring authentication provider which supports authentication tokens of
* type {@link PreAuthenticatedAuthenticationToken} created by the
* {@link ControllerPreAuthenticatedSecurityHeaderFilter}.
*
*
* Additionally to the authentication token providing the principal and the
* credentials which must be match, this authentication provider can also check
* the remote IP address of the request.
*
*
* E.g. The request path is /controller/v1/{controllerId} then the controllerId
* in the path is the principal. The credentials are the extracted information
* from e.g. a certificate provided by an reverse proxy. Due this request is
* only allowed from a specific source address this authentication manager can
* also check the remote IP address of the request.
*
*
*
*
*/
@@ -58,7 +58,7 @@ public class PreAuthTokenSourceTrustAuthenticationProvider implements Authentica
* Creates a new PreAuthTokenSourceTrustAuthenticationProvider with given
* source IP addresses which are trusted and should be checked against the
* request remote IP address.
*
*
* @param authorizedSourceIps
* a list of IP addresses.
*/
@@ -70,7 +70,7 @@ public class PreAuthTokenSourceTrustAuthenticationProvider implements Authentica
* Creates a new PreAuthTokenSourceTrustAuthenticationProvider with given
* source IP addresses which are trusted and should be checked against the
* request remote IP address.
*
*
* @param authorizedSourceIps
* a list of IP addresses.
*/
@@ -87,7 +87,6 @@ public class PreAuthTokenSourceTrustAuthenticationProvider implements Authentica
return null;
}
boolean successAuthentication = false;
final PreAuthenticatedAuthenticationToken token = (PreAuthenticatedAuthenticationToken) authentication;
final Object credentials = token.getCredentials();
final Object principal = token.getPrincipal();
@@ -97,14 +96,7 @@ public class PreAuthTokenSourceTrustAuthenticationProvider implements Authentica
throw new BadCredentialsException("The provided principal and credentials are not match");
}
// check if principal equals credentials because we want to check if
// e.g. controllerId
// containing in the URL equals the controllerId in the special header
// set by the reverse
// proxy which extracted the CN from the certificate
if (principal.equals(credentials)) {
successAuthentication = checkSourceIPAddressIfNeccessary(tokenDetails);
}
boolean successAuthentication = calculateAuthenticationSuccess(principal, credentials, tokenDetails);
if (successAuthentication) {
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");
}
/**
*
* 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) {
boolean success = authorizedSourceIps == null;
String remoteAddress = null;

View File

@@ -22,7 +22,7 @@ public interface PreAuthentificationFilter {
/**
* Check if the filter is enabled.
*
*
* @param secruityToken
* the secruity info
* @return <true> is enabled <false> diabled
@@ -31,7 +31,7 @@ public interface PreAuthentificationFilter {
/**
* Extract the principal information from the current secruityToken.
*
*
* @param secruityToken
* the secruityToken
* @return the extracted tenant and controller id
@@ -40,17 +40,17 @@ public interface PreAuthentificationFilter {
/**
* Extract the principal credentials from the current secruityToken.
*
*
* @param secruityToken
* the secruityToken
* @return the extracted tenant and controller id
*/
HeaderAuthentication getPreAuthenticatedCredentials(TenantSecurityToken secruityToken);
Object getPreAuthenticatedCredentials(TenantSecurityToken secruityToken);
/**
* Allows to add additional authorities to the successful authenticated
* token.
*
*
* @return the authorities granted to the principal, or an empty collection
* if the token has not been authenticated. Never null.
* @see Authentication#getAuthorities()

View File

@@ -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;
}
}

View File

@@ -8,9 +8,7 @@
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>
<parent>
<groupId>org.eclipse.hawkbit</groupId>
@@ -22,60 +20,63 @@
<packaging>pom</packaging>
<build>
<plugins>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>2.7</version>
<executions>
<execution>
<id>copy-resources</id>
<phase>verify</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<profiles>
<profile>
<id>generateTestReport</id>
<build>
<plugins>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>copy-resources</id>
<phase>verify</phase>
<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>
<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>
<descriptors>
<descriptor>src/main/assembly/test-report.xml</descriptor>
</descriptors>
<appendAssemblyId>false</appendAssemblyId>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.5.3</version>
<configuration>
<descriptors>
<descriptor>src/main/assembly/test-report.xml</descriptor>
</descriptors>
<appendAssemblyId>false</appendAssemblyId>
</configuration>
<executions>
<execution>
<id>create-report-zip-assembly</id>
<phase>verify</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<executions>
<execution>
<id>create-report-zip-assembly</id>
<phase>verify</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@@ -263,9 +263,9 @@ public class CommonDialogWindow extends Window {
if (field instanceof Table) {
((Table) field).addItemSetChangeListener(new ChangeListener(field));
} else {
field.addValueChangeListener(new ChangeListener(field));
}
field.addValueChangeListener(new ChangeListener(field));
}
}

View File

@@ -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"));
}
}

View File

@@ -10,6 +10,9 @@ package org.eclipse.hawkbit.ui.components;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.ui.common.DistributionSetIdName;
import org.eclipse.hawkbit.ui.common.UserDetailsFormatter;
import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil;
import org.eclipse.hawkbit.ui.utils.SPDateTimeUtil;
/**
* Proxy for {@link DistributionSet}.
@@ -39,6 +42,33 @@ public class ProxyDistribution {
private String version;
private String description;
/**
* Creates an empty proxy distribution set
*/
public ProxyDistribution() {
// Default constructor
}
/**
* Creates a new proxy distribution set by using the values from a distribution set
* @param distributionSet the source distribution set
*/
public ProxyDistribution(DistributionSet distributionSet) {
setName(distributionSet.getName());
setDescription(distributionSet.getDescription());
setDistId(distributionSet.getId());
setId(distributionSet.getId());
setVersion(distributionSet.getVersion());
setCreatedDate(SPDateTimeUtil.getFormattedDate(distributionSet.getCreatedAt()));
setLastModifiedDate(SPDateTimeUtil.getFormattedDate(distributionSet.getLastModifiedAt()));
setCreatedByUser(UserDetailsFormatter.loadAndFormatCreatedBy(distributionSet));
setModifiedByUser(UserDetailsFormatter.loadAndFormatLastModifiedBy(distributionSet));
setNameVersion(
HawkbitCommonUtil.getFormattedNameVersion(distributionSet.getName(), distributionSet.getVersion()));
setIsComplete(distributionSet.isComplete());
}
/**
* @return the nameVersion
*/

View File

@@ -26,6 +26,7 @@ public class ProxyTargetFilter {
private String createdBy;
private String lastModifiedBy;
private String query;
private ProxyDistribution autoAssignDistributionSet;
public String getCreatedDate() {
return createdDate;
@@ -90,4 +91,11 @@ public class ProxyTargetFilter {
this.createdBy = createdBy;
}
public ProxyDistribution getAutoAssignDistributionSet() {
return autoAssignDistributionSet;
}
public void setAutoAssignDistributionSet(ProxyDistribution autoAssignDistributionSet) {
this.autoAssignDistributionSet = autoAssignDistributionSet;
}
}

View File

@@ -25,6 +25,7 @@ import org.eclipse.hawkbit.ui.common.DistributionSetIdName;
import org.eclipse.hawkbit.ui.common.detailslayout.AbstractNamedVersionedEntityTableDetailsLayout;
import org.eclipse.hawkbit.ui.common.detailslayout.DistributionSetMetadatadetailslayout;
import org.eclipse.hawkbit.ui.common.detailslayout.SoftwareModuleDetailsTable;
import org.eclipse.hawkbit.ui.common.detailslayout.TargetFilterQueryDetailsTable;
import org.eclipse.hawkbit.ui.common.tagdetails.DistributionTagToken;
import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleSmallNoBorder;
@@ -91,6 +92,8 @@ public class DistributionSetDetails extends AbstractNamedVersionedEntityTableDet
private DistributionSetMetadatadetailslayout dsMetadataTable;
private TargetFilterQueryDetailsTable tfqDetailsTable;
private VerticalLayout tagsLayout;
private Map<String, StringBuilder> assignedSWModule;
@@ -103,9 +106,14 @@ public class DistributionSetDetails extends AbstractNamedVersionedEntityTableDet
softwareModuleTable = new SoftwareModuleDetailsTable();
softwareModuleTable.init(getI18n(), true, getPermissionChecker(), distributionSetManagement, getEventBus(),
manageDistUIState);
dsMetadataTable = new DistributionSetMetadatadetailslayout();
dsMetadataTable.init(getI18n(), getPermissionChecker(), distributionSetManagement, dsMetadataPopupLayout,
entityFactory);
tfqDetailsTable = new TargetFilterQueryDetailsTable();
tfqDetailsTable.init(getI18n());
super.init();
}
@@ -120,6 +128,7 @@ public class DistributionSetDetails extends AbstractNamedVersionedEntityTableDet
populateModule();
populateTags();
populateMetadataDetails();
populateTargetFilterQueries();
}
private void populateModule() {
@@ -277,6 +286,10 @@ public class DistributionSetDetails extends AbstractNamedVersionedEntityTableDet
dsMetadataTable.populateDSMetadata(getSelectedBaseEntity());
}
protected void populateTargetFilterQueries() {
tfqDetailsTable.populateTableByDistributionSet(getSelectedBaseEntity());
}
private void updateDistributionSetDetailsLayout(final String type, final Boolean isMigrationRequired) {
final VerticalLayout detailsTabLayout = getDetailsLayout();
detailsTabLayout.removeAllComponents();
@@ -332,6 +345,7 @@ public class DistributionSetDetails extends AbstractNamedVersionedEntityTableDet
detailsTab.addTab(createTagsLayout(), getI18n().get("caption.tags.tab"), null);
detailsTab.addTab(createLogLayout(), getI18n().get("caption.logs.tab"), null);
detailsTab.addTab(dsMetadataTable, getI18n().get("caption.metadata"), null);
detailsTab.addTab(tfqDetailsTable, getI18n().get("caption.auto.assignment.ds"), null);
}
@Override

View File

@@ -20,10 +20,8 @@ import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetFilter;
import org.eclipse.hawkbit.repository.model.DistributionSetFilter.DistributionSetFilterBuilder;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.ui.common.UserDetailsFormatter;
import org.eclipse.hawkbit.ui.components.ProxyDistribution;
import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil;
import org.eclipse.hawkbit.ui.utils.SPDateTimeUtil;
import org.eclipse.hawkbit.ui.utils.SPUIDefinitions;
import org.eclipse.hawkbit.ui.utils.SpringContextHelper;
import org.springframework.data.domain.Page;
@@ -43,11 +41,12 @@ public class ManageDistBeanQuery extends AbstractBeanQuery<ProxyDistribution> {
private static final long serialVersionUID = 5176481314404662215L;
private Sort sort = new Sort(Direction.ASC, "createdAt");
private String searchText = null;
private String searchText;
private transient DistributionSetManagement distributionSetManagement;
private transient Page<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)) {
searchText = String.format("%%%s%%", searchText);
}
if (null != queryConfig.get(SPUIDefinitions.FILTER_BY_DISTRIBUTION_SET_TYPE)) {
if (queryConfig.get(SPUIDefinitions.FILTER_BY_DISTRIBUTION_SET_TYPE) != null) {
distributionSetType = (DistributionSetType) queryConfig
.get(SPUIDefinitions.FILTER_BY_DISTRIBUTION_SET_TYPE);
}
if(queryConfig.get(SPUIDefinitions.FILTER_BY_DS_COMPLETE) != null) {
dsComplete = (Boolean)queryConfig.get(SPUIDefinitions.FILTER_BY_DS_COMPLETE);
}
}
if (sortStates.length > 0) {
// Initalize sort
// Initialize sort
sort = new Sort(sortStates[0] ? Direction.ASC : Direction.DESC, (String) sortPropertyIds[0]);
// Add sort
for (int distId = 1; distId < sortPropertyIds.length; distId++) {
@@ -96,27 +98,17 @@ public class ManageDistBeanQuery extends AbstractBeanQuery<ProxyDistribution> {
} else if (Strings.isNullOrEmpty(searchText)) {
// if no search filters available
distBeans = getDistributionSetManagement().findDistributionSetsByDeletedAndOrCompleted(
new OffsetBasedPageRequest(startIndex, count, sort), false, null);
new OffsetBasedPageRequest(startIndex, count, sort), false, dsComplete);
} else {
final DistributionSetFilter distributionSetFilter = new DistributionSetFilterBuilder().setIsDeleted(false)
.setIsComplete(dsComplete)
.setSearchText(searchText).setSelectDSWithNoTag(Boolean.FALSE).setType(distributionSetType).build();
distBeans = getDistributionSetManagement().findDistributionSetsByFilters(
new PageRequest(startIndex / count, count, sort), distributionSetFilter);
}
for (final DistributionSet distributionSet : distBeans) {
final ProxyDistribution proxyDistribution = new ProxyDistribution();
proxyDistribution.setName(distributionSet.getName());
proxyDistribution.setDescription(distributionSet.getDescription());
proxyDistribution.setDistId(distributionSet.getId());
proxyDistribution.setId(distributionSet.getId());
proxyDistribution.setVersion(distributionSet.getVersion());
proxyDistribution.setCreatedDate(SPDateTimeUtil.getFormattedDate(distributionSet.getCreatedAt()));
proxyDistribution.setLastModifiedDate(SPDateTimeUtil.getFormattedDate(distributionSet.getLastModifiedAt()));
proxyDistribution.setCreatedByUser(UserDetailsFormatter.loadAndFormatCreatedBy(distributionSet));
proxyDistribution.setModifiedByUser(UserDetailsFormatter.loadAndFormatLastModifiedBy(distributionSet));
proxyDistribution.setIsComplete(distributionSet.isComplete());
proxyDistributions.add(proxyDistribution);
proxyDistributions.add(new ProxyDistribution(distributionSet));
}
return proxyDistributions;
}
@@ -132,9 +124,10 @@ public class ManageDistBeanQuery extends AbstractBeanQuery<ProxyDistribution> {
if (Strings.isNullOrEmpty(searchText) && null == distributionSetType) {
// if no search filters available
firstPageDistributionSets = getDistributionSetManagement().findDistributionSetsByDeletedAndOrCompleted(
new PageRequest(0, SPUIDefinitions.PAGE_SIZE, sort), false, null);
new PageRequest(0, SPUIDefinitions.PAGE_SIZE, sort), false, dsComplete);
} else {
final DistributionSetFilter distributionSetFilter = new DistributionSetFilterBuilder().setIsDeleted(false)
.setIsComplete(dsComplete)
.setSearchText(searchText).setSelectDSWithNoTag(Boolean.FALSE).setType(distributionSetType).build();
firstPageDistributionSets = getDistributionSetManagement().findDistributionSetsByFilters(
new PageRequest(0, SPUIDefinitions.PAGE_SIZE, sort), distributionSetFilter);

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -13,8 +13,10 @@ import java.util.List;
import java.util.Map;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.ui.common.UserDetailsFormatter;
import org.eclipse.hawkbit.ui.components.ProxyDistribution;
import org.eclipse.hawkbit.ui.components.ProxyTargetFilter;
import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil;
import org.eclipse.hawkbit.ui.utils.SPDateTimeUtil;
@@ -85,7 +87,7 @@ public class TargetFilterBeanQuery extends AbstractBeanQuery<ProxyTargetFilter>
targetFilterQuery = getTargetFilterQueryManagement().findAllTargetFilterQuery(
new PageRequest(startIndex / SPUIDefinitions.PAGE_SIZE, SPUIDefinitions.PAGE_SIZE, sort));
} else {
targetFilterQuery = getTargetFilterQueryManagement().findTargetFilterQueryByFilters(
targetFilterQuery = getTargetFilterQueryManagement().findTargetFilterQueryByName(
new PageRequest(startIndex / SPUIDefinitions.PAGE_SIZE, SPUIDefinitions.PAGE_SIZE, sort),
searchText);
}
@@ -98,6 +100,11 @@ public class TargetFilterBeanQuery extends AbstractBeanQuery<ProxyTargetFilter>
proxyTarFilter.setModifiedDate(SPDateTimeUtil.getFormattedDate(tarFilterQuery.getLastModifiedAt()));
proxyTarFilter.setLastModifiedBy(UserDetailsFormatter.loadAndFormatLastModifiedBy(tarFilterQuery));
proxyTarFilter.setQuery(tarFilterQuery.getQuery());
final DistributionSet distributionSet = tarFilterQuery.getAutoAssignDistributionSet();
if (distributionSet != null) {
proxyTarFilter.setAutoAssignDistributionSet(new ProxyDistribution(distributionSet));
}
proxyTargetFilter.add(proxyTarFilter);
}
return proxyTargetFilter;
@@ -117,7 +124,7 @@ public class TargetFilterBeanQuery extends AbstractBeanQuery<ProxyTargetFilter>
.findAllTargetFilterQuery(new PageRequest(0, SPUIDefinitions.PAGE_SIZE, sort));
} else {
firstPageTargetFilter = getTargetFilterQueryManagement()
.findTargetFilterQueryByFilters(new PageRequest(0, SPUIDefinitions.PAGE_SIZE, sort), searchText);
.findTargetFilterQueryByName(new PageRequest(0, SPUIDefinitions.PAGE_SIZE, sort), searchText);
}
final long size = firstPageTargetFilter.getTotalElements();

View File

@@ -10,16 +10,21 @@ package org.eclipse.hawkbit.ui.filtermanagement;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import com.vaadin.ui.Button;
import com.vaadin.ui.Link;
import com.vaadin.ui.Table;
import com.vaadin.ui.UI;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.ui.common.ConfirmationDialog;
import org.eclipse.hawkbit.ui.components.ProxyDistribution;
import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleSmallNoBorder;
import org.eclipse.hawkbit.ui.filtermanagement.event.CustomFilterUIEvent;
@@ -44,15 +49,11 @@ import com.vaadin.data.Item;
import com.vaadin.server.FontAwesome;
import com.vaadin.spring.annotation.SpringComponent;
import com.vaadin.spring.annotation.ViewScope;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Link;
import com.vaadin.ui.Table;
import com.vaadin.ui.UI;
import com.vaadin.ui.themes.ValoTheme;
/**
*
* Displays list of target filter queries
*
*/
@SpringComponent
@@ -76,6 +77,12 @@ public class TargetFilterTable extends Table {
@Autowired
private transient TargetFilterQueryManagement targetFilterQueryManagement;
@Autowired
private transient DistributionSetManagement distributionSetManagement;
@Autowired
private DistributionSetSelectWindow dsSelectWindow;
private Container container;
private static final int PROPERTY_DEPT = 3;
@@ -142,15 +149,17 @@ public class TargetFilterTable extends Table {
container.addContainerProperty(SPUILabelDefinitions.VAR_CREATED_DATE, Date.class, null);
container.addContainerProperty(SPUILabelDefinitions.VAR_MODIFIED_DATE, Date.class, null);
container.addContainerProperty(SPUILabelDefinitions.VAR_MODIFIED_BY, String.class, null);
container.addContainerProperty(SPUILabelDefinitions.AUTO_ASSIGN_DISTRIBUTION_SET, String.class, null);
}
private List<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.VAR_CREATED_USER, i18n.get("header.createdBy"), 0.15F));
columnList.add(new TableColumn(SPUILabelDefinitions.VAR_CREATED_USER, i18n.get("header.createdBy"), 0.1F));
columnList.add(new TableColumn(SPUILabelDefinitions.VAR_CREATED_DATE, i18n.get("header.createdDate"), 0.2F));
columnList.add(new TableColumn(SPUILabelDefinitions.VAR_MODIFIED_BY, i18n.get("header.modifiedBy"), 0.15F));
columnList.add(new TableColumn(SPUILabelDefinitions.VAR_MODIFIED_BY, i18n.get("header.modifiedBy"), 0.1F));
columnList.add(new TableColumn(SPUILabelDefinitions.VAR_MODIFIED_DATE, i18n.get("header.modifiedDate"), 0.2F));
columnList.add(new TableColumn(SPUILabelDefinitions.AUTO_ASSIGN_DISTRIBUTION_SET, i18n.get("header.auto.assignment.ds"), 0.1F));
columnList.add(new TableColumn(SPUIDefinitions.CUSTOM_FILTER_DELETE, i18n.get("header.delete"), 0.1F));
return columnList;
@@ -172,7 +181,7 @@ public class TargetFilterTable extends Table {
return deleteIcon;
}
private String getDeleteIconId(final String targetFilterName) {
private static String getDeleteIconId(final String targetFilterName) {
return new StringBuilder(UIComponentIdProvider.CUSTOM_FILTER_DELETE_ICON).append('.').append(targetFilterName)
.toString();
}
@@ -208,6 +217,9 @@ public class TargetFilterTable extends Table {
addGeneratedColumn(SPUILabelDefinitions.NAME,
(source, itemId, columnId) -> customFilterDetailButton((Long) itemId));
addGeneratedColumn(SPUILabelDefinitions.AUTO_ASSIGN_DISTRIBUTION_SET,
(source, itemId, columnId) -> customFilterDistributionSetButton((Long) itemId));
}
private Button customFilterDetailButton(final Long itemId) {
@@ -222,6 +234,34 @@ public class TargetFilterTable extends Table {
return updateIcon;
}
private Button customFilterDistributionSetButton(final Long itemId) {
final Item row1 = getItem(itemId);
final ProxyDistribution distSet = (ProxyDistribution) row1.getItemProperty(SPUILabelDefinitions.AUTO_ASSIGN_DISTRIBUTION_SET).getValue();
final String buttonId = "distSetButton";
Button updateIcon;
if(distSet == null) {
updateIcon = SPUIComponentProvider.getButton(buttonId, i18n.get("button.no.auto.assignment"),
i18n.get("button.auto.assignment.desc"), null, false, null, SPUIButtonStyleSmallNoBorder.class);
} else {
updateIcon = SPUIComponentProvider.getButton(buttonId, distSet.getNameVersion(),
i18n.get("button.auto.assignment.desc"), null, false, null, SPUIButtonStyleSmallNoBorder.class);
}
updateIcon.addClickListener(this::onClickOfDistributionSetButton);
updateIcon.setData(row1);
updateIcon.addStyleName(ValoTheme.LINK_SMALL + " " + "on-focus-no-border link");
return updateIcon;
}
private void onClickOfDistributionSetButton(final ClickEvent event) {
final Item item = (Item) ((Button) event.getComponent()).getData();
final Long tfqId = (Long)item.getItemProperty(SPUILabelDefinitions.VAR_ID).getValue();
dsSelectWindow.showForTargetFilter(tfqId);
}
private void onClickOfDetailButton(final ClickEvent event) {
final String targetFilterName = (String) ((Button) event.getComponent()).getData();
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
@@ -246,14 +286,11 @@ public class TargetFilterTable extends Table {
}
private void setColumnProperties() {
final List<TableColumn> columnList = getVisbleColumns();
final List<Object> swColumnIds = new ArrayList<>();
for (final TableColumn column : columnList) {
setVisibleColumns(getVisbleColumns().stream().map(column -> {
setColumnHeader(column.getColumnPropertyId(), column.getColumnHeader());
setColumnExpandRatio(column.getColumnPropertyId(), column.getExpandRatio());
swColumnIds.add(column.getColumnPropertyId());
}
setVisibleColumns(swColumnIds.toArray());
return column.getColumnPropertyId();
}).toArray());
}
}

View File

@@ -63,13 +63,17 @@ public class CertificateAuthenticationConfigurationItem extends AbstractAuthenti
final Label caRootAuthorityLabel = new LabelBuilder().name("SSL Issuer Hash:").buildLabel();
caRootAuthorityLabel.setDescription(
"The SSL Issuer iRules.X509 hash, to validate against the controller request certifcate.");
caRootAuthorityLabel.setWidthUndefined();
caRootAuthorityTextField = new TextFieldBuilder().immediate(true).maxLengthAllowed(128).buildTextComponent();
caRootAuthorityTextField.setWidth("500px");
caRootAuthorityTextField = new TextFieldBuilder().immediate(true).maxLengthAllowed(160).buildTextComponent();
caRootAuthorityTextField.setWidth("100%");
caRootAuthorityTextField.addTextChangeListener(event -> caRootAuthorityChanged());
caRootAuthorityLayout.addComponent(caRootAuthorityLabel);
caRootAuthorityLayout.setExpandRatio(caRootAuthorityLabel, 0);
caRootAuthorityLayout.addComponent(caRootAuthorityTextField);
caRootAuthorityLayout.setExpandRatio(caRootAuthorityTextField, 1);
caRootAuthorityLayout.setWidth("100%");
detailLayout.addComponent(caRootAuthorityLayout);

View File

@@ -487,6 +487,11 @@ public final class SPUIDefinitions {
*/
public static final String FILTER_BY_INVALID_QUERY = "FilterByInvalidFilterQueryText";
/**
* Filter by distribution set complete.
*/
public static final String FILTER_BY_DS_COMPLETE = "FilterByDistributionSetComplete";
/**
* Sort order of column - created at in target table.
*/

View File

@@ -203,6 +203,10 @@ public final class SPUILabelDefinitions {
* ASSIGNED DISTRIBUTION ID.
*/
public static final String ASSIGNED_DISTRIBUTION_ID = "assignedDistributionSet.id";
/**
* AUTO ASSIGN DISTRIBUTION SET ID
*/
public static final String AUTO_ASSIGN_DISTRIBUTION_SET = "autoAssignDistributionSet";
/**
* ASSIGNED DISTRIBUTION Name & Version.
*/

View File

@@ -885,6 +885,26 @@ public final class UIComponentIdProvider {
*/
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.
*/

View File

@@ -168,6 +168,7 @@
.v-table-cell-content {
height: 27px;
font-size: 12px;
}
}

View File

@@ -22,8 +22,11 @@ button.no.actions = No actions
button.ok = OK
button.cancel = Cancel
button.upload.file = Upload File
button.no.auto.assignment = none
button.auto.assignment.desc = Select auto assign distribution set
bulk.targets.upload = Please upload csv file.
bulkupload.ds.name = DS Name
button.discard=Discard
# Headers prefix with - header
header.target.table=Targets
@@ -48,6 +51,14 @@ header.dist.twintable.selected=Selected
header.dist.twintable.available=Available
header.target.installed = Installed
header.target.assigned = Assigned
header.type=Type
header.swmodules=SwModules
header.migrations.step=IsRequiredMigrationStep
header.action=Actions
header.action.run=Run
header.action.pause=Pause
header.action.update=Edit
header.status=Status
# Captions prefix with - caption
caption.action.history = Action history for {0}
@@ -59,6 +70,7 @@ caption.filter.simple = Simple Filter
caption.filter.custom = Custom Filter
caption.metadata = Metadata
caption.select.auto.assign.dist = Select auto assignment distribution set
caption.add.softwaremodule = Configure Software Module
caption.add.new.dist = Configure New Distribution
caption.update.dist = Configure Update Distribution
@@ -92,7 +104,8 @@ caption.confirm.abort.action = Confirm Abort Action
caption.filter.delete.confirmbox = Confirm Filter Delete Action
caption.metadata.popup = Metadata of
caption.metadata.delete.action.confirmbox = Confirm Metadata Delete Action
caption.confirm.assign.consequences = Auto assign consequences
caption.auto.assignment.ds = Auto assignment
# Labels prefix with - label
label.dist.details.type = Type :
@@ -129,8 +142,6 @@ 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 :
@@ -161,13 +172,15 @@ 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
label.auto.assign.description=When an auto assign distribution set is selected, it will be automatically assigned to all targets that match the target filter.
label.auto.assign.enable=Enable auto assignment
label.scheduled=Scheduled
# Checkbox label prefix with - checkbox
checkbox.dist.migration.required = Required Migration Step :
@@ -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.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 !
@@ -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.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!
@@ -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.bulk.upload.tag.assignment.failed = Tag {0} assignment failed as tag no longer exists
message.bulk.upload.tag.assignments.failed= Few tag assignments failed as tags no longer exists
message.confirm.assign.consequences.none = This auto assignment will not have any effect on the currently available targets. In future added targets might match the filter and will receive the selected distribution set automatically.
message.confirm.assign.consequences.text = When you confirm this auto assignment, {0} targets which match the filter will immediately get assigned with the selected distribution set.
# action info
action.target.table.selectall = Select all (Ctrl+A)
@@ -450,12 +463,12 @@ header.assigned.ds = Assigned DS
header.installed.ds = Installed DS
header.target.status = Status
header.target.tags = Tags
header.distributionset = Distribution set
header.numberofgroups = No. of groups
header.detail.status = Detail status
header.total.targets = Total targets
header.key = Key
header.value = Value
header.auto.assignment.ds = Auto assignment
header.target.filter.name = Target filter name
header.target.filter.query = Target filter query
distribution.details.header = Distribution set
target.details.header = Target
@@ -512,3 +525,9 @@ menu.title = Software Provisioning
#Target Filter Management
breadcrumb.target.filter.custom.filters = Custom Filters
notification.configuration.save=Saved changes
controller.polling.title=Polling Configuration
controller.polling.time=Polling Time
controller.polling.overduetime=Polling Overdue Time

View File

@@ -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

View File

@@ -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

View File

@@ -149,7 +149,7 @@
<!-- Misc libraries versions - END -->
<!-- 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.url>https://github.com/eclipse/hawkbit.git</release.scm.url>
<!-- Release - END -->