Merge branch 'master' into feature_target_filtering_supports_overdue
Conflicts: hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/RepositoryApplicationConfiguration.java hawkbit-ui/src/main/resources/messages_de.properties hawkbit-ui/src/main/resources/messages_en.properties
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -38,6 +38,10 @@ local.properties
|
||||
.settings/
|
||||
.loadpath
|
||||
|
||||
# IDEA
|
||||
*.iml
|
||||
.idea
|
||||
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
[hawkBit](https://projects.eclipse.org/projects/iot.hawkbit) is an domain independent back end solution for rolling out software updates to constrained edge devices as well as more powerful controllers and gateways connected to IP based networking infrastructure.
|
||||
|
||||
Build: [](https://circleci.com/gh/eclipse/hawkbit)
|
||||
[](https://sonar.eu-gb.mybluemix.net)
|
||||
[](https://sonar.eu-gb.mybluemix.net)[](https://maven-badges.herokuapp.com/maven-central/org.eclipse.hawkbit/hawkbit-parent)
|
||||
|
||||
# Documentation
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Describing the fields of the Target model which can be used in the REST API
|
||||
* e.g. for sorting etc.
|
||||
*
|
||||
*/
|
||||
public enum TargetFilterQueryFields implements FieldNameProvider {
|
||||
|
||||
/**
|
||||
* The id field.
|
||||
*/
|
||||
ID("id"),
|
||||
|
||||
/**
|
||||
* The name field.
|
||||
*/
|
||||
NAME("name"),
|
||||
|
||||
/**
|
||||
* distribution set which is set as auto assign distribution set
|
||||
*/
|
||||
AUTOASSIGNDISTRIBUTIONSET("autoAssignDistributionSet", "name", "version");
|
||||
|
||||
|
||||
private final String fieldName;
|
||||
private List<String> subEntityAttributes;
|
||||
private boolean mapField;
|
||||
|
||||
TargetFilterQueryFields(final String fieldName) {
|
||||
this(fieldName, false, Collections.emptyList());
|
||||
}
|
||||
|
||||
TargetFilterQueryFields(final String fieldName, final String... subEntityAttribues) {
|
||||
this(fieldName, false, Arrays.asList(subEntityAttribues));
|
||||
}
|
||||
|
||||
TargetFilterQueryFields(final String fieldName, final boolean mapField, final List<String> subEntityAttribues) {
|
||||
this.fieldName = fieldName;
|
||||
this.mapField = mapField;
|
||||
this.subEntityAttributes = subEntityAttribues;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getSubEntityAttributes() {
|
||||
return subEntityAttributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMap() {
|
||||
return mapField;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFieldName() {
|
||||
return fieldName;
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,8 @@ import org.springframework.security.authentication.InsufficientAuthenticationExc
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.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);
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.mgmt.json.model.targetfilter;
|
||||
|
||||
import org.eclipse.hawkbit.mgmt.json.model.MgmtBaseEntity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* A json annotated rest model for Target Filter Queries to RESTful API
|
||||
* representation.
|
||||
*
|
||||
*/
|
||||
@JsonInclude(Include.ALWAYS)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class MgmtTargetFilterQuery extends MgmtBaseEntity {
|
||||
|
||||
@JsonProperty(value = "id", required = true)
|
||||
private Long filterId;
|
||||
|
||||
@JsonProperty
|
||||
private String name;
|
||||
|
||||
@JsonProperty
|
||||
private String query;
|
||||
|
||||
@JsonProperty
|
||||
private Long autoAssignDistributionSet;
|
||||
|
||||
public Long getFilterId() {
|
||||
return filterId;
|
||||
}
|
||||
|
||||
public void setFilterId(final Long filterId) {
|
||||
this.filterId = filterId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(final String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getQuery() {
|
||||
return query;
|
||||
}
|
||||
|
||||
public void setQuery(final String query) {
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
public Long getAutoAssignDistributionSet() {
|
||||
return autoAssignDistributionSet;
|
||||
}
|
||||
|
||||
public void setAutoAssignDistributionSet(final Long autoAssignDistributionSet) {
|
||||
this.autoAssignDistributionSet = autoAssignDistributionSet;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.mgmt.json.model.targetfilter;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* Request body for target PUT/POST commands.
|
||||
*
|
||||
*/
|
||||
public class MgmtTargetFilterQueryRequestBody {
|
||||
@JsonProperty(required = true)
|
||||
private String name;
|
||||
|
||||
@JsonProperty(required = true)
|
||||
private String query;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(final String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getQuery() {
|
||||
return query;
|
||||
}
|
||||
|
||||
public void setQuery(String query) {
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtTargetAssignmentR
|
||||
import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.MgmtSoftwareModule;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.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.
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.mgmt.rest.api;
|
||||
|
||||
import org.eclipse.hawkbit.mgmt.json.model.MgmtId;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.PagedList;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSet;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQuery;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQueryRequestBody;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
/**
|
||||
* Api for handling target operations.
|
||||
*/
|
||||
@RequestMapping(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING)
|
||||
public interface MgmtTargetFilterQueryRestApi {
|
||||
|
||||
/**
|
||||
* Handles the GET request of retrieving a single target filter.
|
||||
*
|
||||
* @param filterId
|
||||
* the ID of the target filter to retrieve
|
||||
* @return a single target with status OK.
|
||||
*/
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/{filterId}", produces = { "application/hal+json",
|
||||
MediaType.APPLICATION_JSON_VALUE })
|
||||
ResponseEntity<MgmtTargetFilterQuery> getFilter(@PathVariable("filterId") final Long filterId);
|
||||
|
||||
/**
|
||||
* Handles the GET request of retrieving all filters.
|
||||
*
|
||||
* @param pagingOffsetParam
|
||||
* the offset of list of targets for pagination, might not be
|
||||
* present in the rest request then default value will be applied
|
||||
* @param pagingLimitParam
|
||||
* the limit of the paged request, might not be present in the
|
||||
* rest request then default value will be applied
|
||||
* @param sortParam
|
||||
* the sorting parameter in the request URL, syntax
|
||||
* {@code field:direction, field:direction}
|
||||
* @param rsqlParam
|
||||
* the search parameter in the request URL, syntax
|
||||
* {@code q=name==abc}
|
||||
* @return a list of all targets for a defined or default page request with
|
||||
* status OK. The response is always paged. In any failure the
|
||||
* JsonResponseExceptionHandler is handling the response.
|
||||
*/
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, produces = { "application/hal+json", MediaType.APPLICATION_JSON_VALUE })
|
||||
ResponseEntity<PagedList<MgmtTargetFilterQuery>> getFilters(
|
||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) final int pagingOffsetParam,
|
||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) final int pagingLimitParam,
|
||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) final String sortParam,
|
||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) final String rsqlParam);
|
||||
|
||||
/**
|
||||
* Handles the POST request of creating new target filters. The request body
|
||||
* must always be a list of target filters.
|
||||
*
|
||||
* @param filter
|
||||
* the filters to be created.
|
||||
* @return In case all filters were successfully created the ResponseEntity
|
||||
* with status code 201 with a list of successfully created entities
|
||||
* is returned. In any failure the JsonResponseExceptionHandler is
|
||||
* handling the response.
|
||||
*/
|
||||
@RequestMapping(method = RequestMethod.POST, consumes = { "application/hal+json",
|
||||
MediaType.APPLICATION_JSON_VALUE }, produces = { "application/hal+json", MediaType.APPLICATION_JSON_VALUE })
|
||||
ResponseEntity<MgmtTargetFilterQuery> createFilter(@RequestBody final MgmtTargetFilterQueryRequestBody filter);
|
||||
|
||||
/**
|
||||
* Handles the PUT request of updating a target filter. The ID is within the
|
||||
* URL path of the request. A given ID in the request body is ignored. It's
|
||||
* not possible to set fields to {@code null} values.
|
||||
*
|
||||
* @param filterId
|
||||
* the path parameter which contains the ID of the target filter
|
||||
* @param targetFilterRest
|
||||
* the request body which contains the fields which should be
|
||||
* updated, fields which are not given are ignored for the
|
||||
* update.
|
||||
* @return the updated target filter response which contains all fields
|
||||
* including fields which have not been updated
|
||||
*/
|
||||
@RequestMapping(method = RequestMethod.PUT, value = "/{filterId}", consumes = { "application/hal+json",
|
||||
MediaType.APPLICATION_JSON_VALUE }, produces = { "application/hal+json", MediaType.APPLICATION_JSON_VALUE })
|
||||
ResponseEntity<MgmtTargetFilterQuery> updateFilter(@PathVariable("filterId") final Long filterId,
|
||||
@RequestBody final MgmtTargetFilterQueryRequestBody targetFilterRest);
|
||||
|
||||
/**
|
||||
* Handles the DELETE request of deleting a target filter.
|
||||
*
|
||||
* @param filterId
|
||||
* the ID of the target filter to be deleted
|
||||
* @return If the given controllerId could exists and could be deleted Http
|
||||
* OK. In any failure the JsonResponseExceptionHandler is handling
|
||||
* the response.
|
||||
*/
|
||||
@RequestMapping(method = RequestMethod.DELETE, value = "/{filterId}", produces = { "application/hal+json",
|
||||
MediaType.APPLICATION_JSON_VALUE })
|
||||
ResponseEntity<Void> deleteFilter(@PathVariable("filterId") final Long filterId);
|
||||
|
||||
/**
|
||||
* Handles the GET request of retrieving the distribution set for auto
|
||||
* assignment of an specific target filter.
|
||||
*
|
||||
* @param filterId
|
||||
* the ID of the target to retrieve the assigned distribution
|
||||
* @return the assigned distribution set with status OK, if none is assigned
|
||||
* than {@code null} content (e.g. "{}")
|
||||
*/
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/{filterId}/autoAssignDS", produces = { "application/hal+json",
|
||||
MediaType.APPLICATION_JSON_VALUE })
|
||||
ResponseEntity<MgmtDistributionSet> getAssignedDistributionSet(@PathVariable("filterId") final Long filterId);
|
||||
|
||||
/**
|
||||
* Handles the POST request for changing distribution set for auto
|
||||
* assignment of a target filter.
|
||||
*
|
||||
* @param filterId
|
||||
* of the target to change
|
||||
* @param dsId
|
||||
* of the Id of the auto assign distribution set
|
||||
* @return http status
|
||||
*/
|
||||
@RequestMapping(method = RequestMethod.POST, value = "/{filterId}/autoAssignDS", consumes = {
|
||||
"application/hal+json",
|
||||
MediaType.APPLICATION_JSON_VALUE }, produces = { "application/hal+json", MediaType.APPLICATION_JSON_VALUE })
|
||||
ResponseEntity<MgmtTargetFilterQuery> postAssignedDistributionSet(@PathVariable("filterId") final Long filterId,
|
||||
@RequestBody final MgmtId dsId);
|
||||
|
||||
/**
|
||||
* Handles the DELETE request for removing the distribution set for auto
|
||||
* assignment of a target filter.
|
||||
*
|
||||
* @param filterId
|
||||
* of the target to change
|
||||
* @return http status
|
||||
*/
|
||||
@RequestMapping(method = RequestMethod.DELETE, value = "/{filterId}/autoAssignDS")
|
||||
ResponseEntity<Void> deleteAssignedDistributionSet(@PathVariable("filterId") final Long filterId);
|
||||
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtTargetAssignmentR
|
||||
import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.MgmtSoftwareModule;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.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,
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.mgmt.rest.resource;
|
||||
|
||||
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
|
||||
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQuery;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQueryRequestBody;
|
||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetFilterQueryRestApi;
|
||||
import org.eclipse.hawkbit.repository.EntityFactory;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* A mapper which maps repository model to RESTful model representation and
|
||||
* back.
|
||||
*
|
||||
*/
|
||||
public final class MgmtTargetFilterQueryMapper {
|
||||
|
||||
private MgmtTargetFilterQueryMapper() {
|
||||
// Utility class
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a response for target filter queries.
|
||||
*
|
||||
* @param filters
|
||||
* list of targets
|
||||
* @return the response
|
||||
*/
|
||||
public static List<MgmtTargetFilterQuery> toResponse(final List<TargetFilterQuery> filters) {
|
||||
if (CollectionUtils.isEmpty(filters)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return filters.stream().map(MgmtTargetFilterQueryMapper::toResponse).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a response for target filter query.
|
||||
*
|
||||
* @param filter
|
||||
* the target
|
||||
* @return the response
|
||||
*/
|
||||
public static MgmtTargetFilterQuery toResponse(final TargetFilterQuery filter) {
|
||||
final MgmtTargetFilterQuery targetRest = new MgmtTargetFilterQuery();
|
||||
targetRest.setFilterId(filter.getId());
|
||||
targetRest.setName(filter.getName());
|
||||
targetRest.setQuery(filter.getQuery());
|
||||
|
||||
targetRest.setCreatedBy(filter.getCreatedBy());
|
||||
targetRest.setLastModifiedBy(filter.getLastModifiedBy());
|
||||
|
||||
targetRest.setCreatedAt(filter.getCreatedAt());
|
||||
targetRest.setLastModifiedAt(filter.getLastModifiedAt());
|
||||
|
||||
DistributionSet distributionSet = filter.getAutoAssignDistributionSet();
|
||||
if (distributionSet != null) {
|
||||
targetRest.setAutoAssignDistributionSet(distributionSet.getId());
|
||||
}
|
||||
|
||||
targetRest.add(linkTo(methodOn(MgmtTargetFilterQueryRestApi.class).getFilter(filter.getId())).withRel("self"));
|
||||
targetRest.add(
|
||||
linkTo(methodOn(MgmtTargetFilterQueryRestApi.class).postAssignedDistributionSet(filter.getId(), null))
|
||||
.withRel("autoAssignDS"));
|
||||
|
||||
return targetRest;
|
||||
}
|
||||
|
||||
static TargetFilterQuery fromRequest(final EntityFactory entityFactory,
|
||||
final MgmtTargetFilterQueryRequestBody filterRest) {
|
||||
final TargetFilterQuery filter = entityFactory.generateTargetFilterQuery();
|
||||
filter.setName(filterRest.getName());
|
||||
filter.setQuery(filterRest.getQuery());
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.mgmt.rest.resource;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.hawkbit.mgmt.json.model.MgmtId;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.PagedList;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSet;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQuery;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQueryRequestBody;
|
||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetFilterQueryRestApi;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
||||
import org.eclipse.hawkbit.repository.EntityFactory;
|
||||
import org.eclipse.hawkbit.repository.OffsetBasedPageRequest;
|
||||
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* REST Resource handling target CRUD operations.
|
||||
*/
|
||||
@RestController
|
||||
public class MgmtTargetFilterQueryResource implements MgmtTargetFilterQueryRestApi {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(MgmtTargetFilterQueryResource.class);
|
||||
|
||||
@Autowired
|
||||
private TargetFilterQueryManagement filterManagement;
|
||||
|
||||
@Autowired
|
||||
private DistributionSetManagement distributionSetManagement;
|
||||
|
||||
@Autowired
|
||||
private EntityFactory entityFactory;
|
||||
|
||||
@Override
|
||||
public ResponseEntity<MgmtTargetFilterQuery> getFilter(@PathVariable("filterId") Long filterId) {
|
||||
final TargetFilterQuery findTarget = findFilterWithExceptionIfNotFound(filterId);
|
||||
// to single response include poll status
|
||||
final MgmtTargetFilterQuery response = MgmtTargetFilterQueryMapper.toResponse(findTarget);
|
||||
|
||||
return new ResponseEntity<>(response, HttpStatus.OK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<PagedList<MgmtTargetFilterQuery>> getFilters(
|
||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) int pagingOffsetParam,
|
||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) int pagingLimitParam,
|
||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) String sortParam,
|
||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) String rsqlParam) {
|
||||
|
||||
final int sanitizedOffsetParam = PagingUtility.sanitizeOffsetParam(pagingOffsetParam);
|
||||
final int sanitizedLimitParam = PagingUtility.sanitizePageLimitParam(pagingLimitParam);
|
||||
final Sort sorting = PagingUtility.sanitizeTargetFilterQuerySortParam(sortParam);
|
||||
|
||||
final Pageable pageable = new OffsetBasedPageRequest(sanitizedOffsetParam, sanitizedLimitParam, sorting);
|
||||
final Slice<TargetFilterQuery> findTargetFiltersAll;
|
||||
final Long countTargetsAll;
|
||||
if (rsqlParam != null) {
|
||||
final Page<TargetFilterQuery> findFilterPage = filterManagement
|
||||
.findTargetFilterQueryByFilter(pageable, rsqlParam);
|
||||
countTargetsAll = findFilterPage.getTotalElements();
|
||||
findTargetFiltersAll = findFilterPage;
|
||||
} else {
|
||||
findTargetFiltersAll = filterManagement.findAllTargetFilterQuery(pageable);
|
||||
countTargetsAll = filterManagement.countAllTargetFilterQuery();
|
||||
}
|
||||
|
||||
final List<MgmtTargetFilterQuery> rest = MgmtTargetFilterQueryMapper
|
||||
.toResponse(findTargetFiltersAll.getContent());
|
||||
return new ResponseEntity<>(new PagedList<MgmtTargetFilterQuery>(rest, countTargetsAll), HttpStatus.OK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<MgmtTargetFilterQuery> createFilter(@RequestBody MgmtTargetFilterQueryRequestBody filter) {
|
||||
final TargetFilterQuery createdTarget = filterManagement
|
||||
.createTargetFilterQuery(MgmtTargetFilterQueryMapper.fromRequest(entityFactory, filter));
|
||||
|
||||
return new ResponseEntity<>(MgmtTargetFilterQueryMapper.toResponse(createdTarget), HttpStatus.CREATED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<MgmtTargetFilterQuery> updateFilter(@PathVariable("filterId") Long filterId,
|
||||
@RequestBody MgmtTargetFilterQueryRequestBody targetFilterRest) {
|
||||
|
||||
final TargetFilterQuery existingFilter = findFilterWithExceptionIfNotFound(filterId);
|
||||
LOG.debug("updating target filter query {}", existingFilter.getId());
|
||||
if (targetFilterRest.getName() != null) {
|
||||
existingFilter.setName(targetFilterRest.getName());
|
||||
}
|
||||
if (targetFilterRest.getQuery() != null) {
|
||||
existingFilter.setQuery(targetFilterRest.getQuery());
|
||||
}
|
||||
|
||||
final TargetFilterQuery updateFilter = filterManagement.updateTargetFilterQuery(existingFilter);
|
||||
|
||||
return new ResponseEntity<>(MgmtTargetFilterQueryMapper.toResponse(updateFilter), HttpStatus.OK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Void> deleteFilter(@PathVariable("filterId") Long filterId) {
|
||||
final TargetFilterQuery filter = findFilterWithExceptionIfNotFound(filterId);
|
||||
filterManagement.deleteTargetFilterQuery(filter.getId());
|
||||
LOG.debug("{} target filter query deleted, return status {}", filterId, HttpStatus.OK);
|
||||
return new ResponseEntity<>(HttpStatus.OK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<MgmtTargetFilterQuery> postAssignedDistributionSet(@PathVariable("filterId") Long filterId,
|
||||
@RequestBody MgmtId dsId) {
|
||||
final TargetFilterQuery filter = findFilterWithExceptionIfNotFound(filterId);
|
||||
|
||||
DistributionSet distributionSet;
|
||||
distributionSet = distributionSetManagement.findDistributionSetById(dsId.getId());
|
||||
if (distributionSet == null) {
|
||||
throw new EntityNotFoundException("DistributionSet with Id {" + dsId + "} does not exist");
|
||||
}
|
||||
|
||||
filter.setAutoAssignDistributionSet(distributionSet);
|
||||
|
||||
final TargetFilterQuery updateFilter = filterManagement.updateTargetFilterQuery(filter);
|
||||
|
||||
return new ResponseEntity<>(MgmtTargetFilterQueryMapper.toResponse(updateFilter), HttpStatus.OK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<MgmtDistributionSet> getAssignedDistributionSet(@PathVariable("filterId") Long filterId) {
|
||||
final TargetFilterQuery filter = findFilterWithExceptionIfNotFound(filterId);
|
||||
DistributionSet autoAssignDistributionSet = filter.getAutoAssignDistributionSet();
|
||||
MgmtDistributionSet distributionSetRest = MgmtDistributionSetMapper.toResponse(autoAssignDistributionSet);
|
||||
final HttpStatus retStatus = distributionSetRest == null ? HttpStatus.NO_CONTENT : HttpStatus.OK;
|
||||
return new ResponseEntity<>(distributionSetRest, retStatus);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Void> deleteAssignedDistributionSet(@PathVariable("filterId") Long filterId) {
|
||||
final TargetFilterQuery filter = findFilterWithExceptionIfNotFound(filterId);
|
||||
|
||||
filter.setAutoAssignDistributionSet(null);
|
||||
|
||||
filterManagement.updateTargetFilterQuery(filter);
|
||||
|
||||
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
|
||||
}
|
||||
|
||||
private TargetFilterQuery findFilterWithExceptionIfNotFound(final Long filterId) {
|
||||
final TargetFilterQuery filter = filterManagement.findTargetFilterQueryById(filterId);
|
||||
if (filter == null) {
|
||||
throw new EntityNotFoundException("TargetFilterQuery with Id {" + filterId + "} does not exist");
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import org.eclipse.hawkbit.repository.SoftwareModuleFields;
|
||||
import org.eclipse.hawkbit.repository.SoftwareModuleMetadataFields;
|
||||
import org.eclipse.hawkbit.repository.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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -0,0 +1,362 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.mgmt.rest.resource;
|
||||
|
||||
import static com.google.common.collect.Lists.newArrayList;
|
||||
import static org.fest.assertions.api.Assertions.assertThat;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.not;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.hawkbit.exception.SpServerError;
|
||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||
import org.eclipse.hawkbit.repository.model.*;
|
||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||
import org.eclipse.hawkbit.rest.AbstractRestIntegrationTest;
|
||||
import org.eclipse.hawkbit.rest.exception.MessageNotReadableException;
|
||||
import org.eclipse.hawkbit.rest.json.model.ExceptionInfo;
|
||||
import org.eclipse.hawkbit.rest.util.JsonBuilder;
|
||||
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
|
||||
import org.json.JSONObject;
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
|
||||
import com.jayway.jsonpath.JsonPath;
|
||||
|
||||
import ru.yandex.qatools.allure.annotations.Description;
|
||||
import ru.yandex.qatools.allure.annotations.Features;
|
||||
import ru.yandex.qatools.allure.annotations.Stories;
|
||||
|
||||
/**
|
||||
* Spring MVC Tests against the MgmtTargetResource.
|
||||
*
|
||||
*/
|
||||
@Features("Component Tests - Management API")
|
||||
@Stories("Target Filter Query Resource")
|
||||
public class MgmtTargetFilterQueryResourceTest extends AbstractRestIntegrationTest {
|
||||
|
||||
private static final String TARGET_DESCRIPTION_TEST = "created in test";
|
||||
|
||||
private static final String JSON_PATH_ROOT = "$";
|
||||
|
||||
// fields, attributes
|
||||
private static final String JSON_PATH_FIELD_ID = ".id";
|
||||
private static final String JSON_PATH_FIELD_NAME = ".name";
|
||||
private static final String JSON_PATH_FIELD_QUERY = ".query";
|
||||
private static final String JSON_PATH_FIELD_CONTENT = ".content";
|
||||
private static final String JSON_PATH_FIELD_SIZE = ".size";
|
||||
private static final String JSON_PATH_FIELD_TOTAL = ".total";
|
||||
private static final String JSON_PATH_FIELD_AUTO_ASSIGN_DS = ".autoAssignDistributionSet";
|
||||
|
||||
// target
|
||||
// $.field
|
||||
static final String JSON_PATH_PAGED_LIST_CONTENT = JSON_PATH_ROOT + JSON_PATH_FIELD_CONTENT;
|
||||
static final String JSON_PATH_PAGED_LIST_SIZE = JSON_PATH_ROOT + JSON_PATH_FIELD_SIZE;
|
||||
static final String JSON_PATH_PAGED_LIST_TOTAL = JSON_PATH_ROOT + JSON_PATH_FIELD_TOTAL;
|
||||
|
||||
private static final String JSON_PATH_NAME = JSON_PATH_ROOT + JSON_PATH_FIELD_NAME;
|
||||
private static final String JSON_PATH_ID = JSON_PATH_ROOT + JSON_PATH_FIELD_ID;
|
||||
private static final String JSON_PATH_QUERY = JSON_PATH_ROOT + JSON_PATH_FIELD_QUERY;
|
||||
private static final String JSON_PATH_AUTO_ASSIGN_DS = JSON_PATH_ROOT + JSON_PATH_FIELD_AUTO_ASSIGN_DS;
|
||||
|
||||
|
||||
@Test
|
||||
@Description("Ensures that deletion is executed if permitted.")
|
||||
public void deleteTargetFilterQueryReturnsOK() throws Exception {
|
||||
final String filterName = "filter_01";
|
||||
TargetFilterQuery filterQuery = createSingleTargetFilterQuery(filterName, "name=test_01");
|
||||
|
||||
mvc.perform(delete(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + filterQuery.getId()))
|
||||
.andExpect(status().isOk());
|
||||
|
||||
TargetFilterQuery tfq = targetFilterQueryManagement.findTargetFilterQueryById(filterQuery.getId());
|
||||
assertThat(tfq).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Ensures that deletion is refused with not found if target does not exist.")
|
||||
public void deleteTargetWhichDoesNotExistsLeadsToEntityNotFound() throws Exception {
|
||||
final String notExistingId = "4395";
|
||||
|
||||
mvc.perform(delete(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + notExistingId))
|
||||
.andExpect(status().isNotFound());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Ensures that update is refused with not found if target does not exist.")
|
||||
public void updateTargetWhichDoesNotExistsLeadsToEntityNotFound() throws Exception {
|
||||
final String notExistingId = "4395";
|
||||
mvc.perform(put(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + notExistingId).content("{}")
|
||||
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isNotFound());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Ensures that update request is reflected by repository.")
|
||||
public void updateTargetFilterQueryQuery() throws Exception {
|
||||
final String filterName = "filter_02";
|
||||
final String filterQuery = "name=test_02";
|
||||
final String filterQuery2 = "name=test_02_changed";
|
||||
final String body = new JSONObject().put("query", filterQuery2).toString();
|
||||
|
||||
// prepare
|
||||
TargetFilterQuery tfq = createSingleTargetFilterQuery(filterName, filterQuery);
|
||||
|
||||
mvc.perform(put(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId()).content(body)
|
||||
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.id", equalTo(tfq.getId().intValue())))
|
||||
.andExpect(jsonPath("$.query", equalTo(filterQuery2)))
|
||||
.andExpect(jsonPath("$.name", equalTo(filterName)));
|
||||
|
||||
TargetFilterQuery tfqCheck = targetFilterQueryManagement.findTargetFilterQueryById(tfq.getId());
|
||||
assertThat(tfqCheck.getQuery()).isEqualTo(filterQuery2);
|
||||
assertThat(tfqCheck.getName()).isEqualTo(filterName);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Ensures that update request is reflected by repository.")
|
||||
public void updateTargetFilterQueryName() throws Exception {
|
||||
final String filterName = "filter_03";
|
||||
final String filterName2 = "filter_03_changed";
|
||||
final String filterQuery = "name=test_03";
|
||||
final String body = new JSONObject().put("name", filterName2).toString();
|
||||
|
||||
// prepare
|
||||
TargetFilterQuery tfq = entityFactory.generateTargetFilterQuery();
|
||||
tfq.setName(filterName);
|
||||
tfq.setQuery(filterQuery);
|
||||
tfq = targetFilterQueryManagement.createTargetFilterQuery(tfq);
|
||||
|
||||
mvc.perform(put(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId()).content(body)
|
||||
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.id", equalTo(tfq.getId().intValue())))
|
||||
.andExpect(jsonPath("$.query", equalTo(filterQuery)))
|
||||
.andExpect(jsonPath("$.name", equalTo(filterName2)));
|
||||
|
||||
TargetFilterQuery tfqCheck = targetFilterQueryManagement.findTargetFilterQueryById(tfq.getId());
|
||||
assertThat(tfqCheck.getQuery()).isEqualTo(filterQuery);
|
||||
assertThat(tfqCheck.getName()).isEqualTo(filterName2);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@Description("Ensures that request returns list of filters in defined format.")
|
||||
public void getTargetFilterQueryWithoutAdditionalRequestParameters() throws Exception {
|
||||
final int knownTargetAmount = 3;
|
||||
final String idA = "a";
|
||||
final String idB = "b";
|
||||
final String idC = "c";
|
||||
final String testQuery = "name=test";
|
||||
|
||||
createSingleTargetFilterQuery(idA, testQuery);
|
||||
createSingleTargetFilterQuery(idB, testQuery);
|
||||
createSingleTargetFilterQuery(idC, testQuery);
|
||||
|
||||
mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING)).andExpect(status().isOk())
|
||||
.andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_TOTAL, equalTo(knownTargetAmount)))
|
||||
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_SIZE, equalTo(knownTargetAmount)))
|
||||
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_CONTENT, hasSize(knownTargetAmount)))
|
||||
// idA
|
||||
.andExpect(jsonPath("$.content.[?(@.name==" + idA + ")].name", contains(idA)))
|
||||
.andExpect(jsonPath("$.content.[?(@.name==" + idA + ")].query", contains(testQuery)))
|
||||
// idB
|
||||
.andExpect(jsonPath("$.content.[?(@.name==" + idB + ")].name", contains(idB)))
|
||||
.andExpect(jsonPath("$.content.[?(@.name==" + idB + ")].query", contains(testQuery)))
|
||||
// idC
|
||||
.andExpect(jsonPath("$.content.[?(@.name==" + idC + ")].name", contains(idC)))
|
||||
.andExpect(jsonPath("$.content.[?(@.name==" + idC + ")].query", contains(testQuery)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Ensures that request returns list of filters in defined format in size reduced by given limit parameter.")
|
||||
public void getTargetWithPagingLimitRequestParameter() throws Exception {
|
||||
final int limitSize = 1;
|
||||
final int knownTargetAmount = 3;
|
||||
final String idA = "a";
|
||||
final String idB = "b";
|
||||
final String idC = "c";
|
||||
final String testQuery = "name=test";
|
||||
|
||||
createSingleTargetFilterQuery(idA, testQuery);
|
||||
createSingleTargetFilterQuery(idB, testQuery);
|
||||
createSingleTargetFilterQuery(idC, testQuery);
|
||||
|
||||
mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING)
|
||||
.param(MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, String.valueOf(limitSize)))
|
||||
.andExpect(status().isOk()).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_TOTAL, equalTo(knownTargetAmount)))
|
||||
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_SIZE, equalTo(limitSize)))
|
||||
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_CONTENT, hasSize(limitSize)))
|
||||
// idA
|
||||
.andExpect(jsonPath("$.content.[?(@.name==" + idA + ")].name", contains(idA)))
|
||||
.andExpect(jsonPath("$.content.[?(@.name==" + idA + ")].query", contains(testQuery)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Ensures that request returns list of filters in defined format in size reduced by given limit and offset parameter.")
|
||||
public void getTargetWithPagingLimitAndOffsetRequestParameter() throws Exception {
|
||||
final int knownTargetAmount = 5;
|
||||
final int offsetParam = 2;
|
||||
final int expectedSize = knownTargetAmount - offsetParam;
|
||||
final String idC = "c";
|
||||
final String idD = "d";
|
||||
final String idE = "e";
|
||||
final String testQuery = "name=test";
|
||||
|
||||
createSingleTargetFilterQuery("a", testQuery);
|
||||
createSingleTargetFilterQuery("b", testQuery);
|
||||
createSingleTargetFilterQuery(idC, testQuery);
|
||||
createSingleTargetFilterQuery(idD, testQuery);
|
||||
createSingleTargetFilterQuery(idE, testQuery);
|
||||
|
||||
mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING)
|
||||
.param(MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, String.valueOf(offsetParam))
|
||||
.param(MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, String.valueOf(knownTargetAmount)))
|
||||
.andExpect(status().isOk()).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_TOTAL, equalTo(knownTargetAmount)))
|
||||
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_SIZE, equalTo(expectedSize)))
|
||||
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_CONTENT, hasSize(expectedSize)))
|
||||
// idA
|
||||
.andExpect(jsonPath("$.content.[?(@.name==" + idC + ")].name", contains(idC)))
|
||||
.andExpect(jsonPath("$.content.[?(@.name==" + idC + ")].query", contains(testQuery)))
|
||||
// idB
|
||||
.andExpect(jsonPath("$.content.[?(@.name==" + idD + ")].name", contains(idD)))
|
||||
.andExpect(jsonPath("$.content.[?(@.name==" + idD + ")].query", contains(testQuery)))
|
||||
// idC
|
||||
.andExpect(jsonPath("$.content.[?(@.name==" + idE + ")].name", contains(idE)))
|
||||
.andExpect(jsonPath("$.content.[?(@.name==" + idE + ")].query", contains(testQuery)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSingleTarget() throws Exception {
|
||||
// create first a target which can be retrieved by rest interface
|
||||
final String knownQuery = "name=test01";
|
||||
final String knownName = "someName";
|
||||
final TargetFilterQuery tfq = createSingleTargetFilterQuery(knownName, knownQuery);
|
||||
final String hrefPrefix = "http://localhost"+MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING+"/" + tfq.getId();
|
||||
// test
|
||||
mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId()))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(jsonPath(JSON_PATH_NAME, equalTo(knownName)))
|
||||
.andExpect(jsonPath(JSON_PATH_QUERY, equalTo(knownQuery)))
|
||||
.andExpect(jsonPath("$._links.self.href", equalTo(hrefPrefix)))
|
||||
.andExpect(jsonPath("$._links.autoAssignDS.href", equalTo(hrefPrefix + "/autoAssignDS")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSingleTargetNoExistsResponseNotFound() throws Exception {
|
||||
final String targetIdNotExists = "546546";
|
||||
// test
|
||||
|
||||
final MvcResult mvcResult = mvc
|
||||
.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + targetIdNotExists))
|
||||
.andExpect(status().isNotFound()).andReturn();
|
||||
|
||||
// verify response json exception message
|
||||
final ExceptionInfo exceptionInfo = ResourceUtility
|
||||
.convertException(mvcResult.getResponse().getContentAsString());
|
||||
assertThat(exceptionInfo.getErrorCode()).isEqualTo(SpServerError.SP_REPO_ENTITY_NOT_EXISTS.getKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createTargetFilterQueryWithBadPayloadBadRequest() throws Exception {
|
||||
final String notJson = "abc";
|
||||
|
||||
final MvcResult mvcResult = mvc
|
||||
.perform(post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING).content(notJson)
|
||||
.contentType(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest()).andReturn();
|
||||
|
||||
assertThat(targetFilterQueryManagement.countAllTargetFilterQuery()).isEqualTo(0);
|
||||
|
||||
// verify response json exception message
|
||||
final ExceptionInfo exceptionInfo = ResourceUtility
|
||||
.convertException(mvcResult.getResponse().getContentAsString());
|
||||
assertThat(exceptionInfo.getExceptionClass()).isEqualTo(MessageNotReadableException.class.getName());
|
||||
assertThat(exceptionInfo.getErrorCode()).isEqualTo(SpServerError.SP_REST_BODY_NOT_READABLE.getKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setAutoAssignDistributionSetToTargetFilterQuery() throws Exception {
|
||||
|
||||
final String knownQuery = "name=test05";
|
||||
final String knownName = "filter05";
|
||||
|
||||
final DistributionSet set = testdataFactory.createDistributionSet("one");
|
||||
final TargetFilterQuery tfq = createSingleTargetFilterQuery(knownName, knownQuery);
|
||||
|
||||
mvc.perform(post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/"+tfq.getId()+"/autoAssignDS")
|
||||
.content("{\"id\":" + set.getId() + "}").contentType(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
|
||||
|
||||
assertThat(targetFilterQueryManagement.findTargetFilterQueryById(tfq.getId()).getAutoAssignDistributionSet()).isEqualTo(set);
|
||||
|
||||
final String hrefPrefix = "http://localhost"+MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING+"/" + tfq.getId();
|
||||
|
||||
mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId()))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(jsonPath(JSON_PATH_NAME, equalTo(knownName)))
|
||||
.andExpect(jsonPath(JSON_PATH_QUERY, equalTo(knownQuery)))
|
||||
.andExpect(jsonPath(JSON_PATH_AUTO_ASSIGN_DS, equalTo(set.getId().intValue())))
|
||||
.andExpect(jsonPath("$._links.self.href", equalTo(hrefPrefix)))
|
||||
.andExpect(jsonPath("$._links.autoAssignDS.href", equalTo(hrefPrefix + "/autoAssignDS")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteAutoAssignDistributionSetOfTargetFilterQuery() throws Exception {
|
||||
|
||||
final String knownQuery = "name=test06";
|
||||
final String knownName = "filter06";
|
||||
final String dsName = "testDS";
|
||||
|
||||
final DistributionSet set = testdataFactory.createDistributionSet(dsName);
|
||||
TargetFilterQuery tfq = entityFactory.generateTargetFilterQuery();
|
||||
tfq.setName(knownName);
|
||||
tfq.setQuery(knownQuery);
|
||||
tfq.setAutoAssignDistributionSet(set);
|
||||
tfq = targetFilterQueryManagement.createTargetFilterQuery(tfq);
|
||||
|
||||
assertThat(targetFilterQueryManagement.findTargetFilterQueryById(tfq.getId()).getAutoAssignDistributionSet())
|
||||
.isEqualTo(set);
|
||||
|
||||
mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/"+tfq.getId()+"/autoAssignDS"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath(JSON_PATH_NAME, equalTo(dsName)));
|
||||
|
||||
mvc.perform(delete(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/"+tfq.getId()+"/autoAssignDS"))
|
||||
.andExpect(status().isNoContent());
|
||||
|
||||
assertThat(targetFilterQueryManagement.findTargetFilterQueryById(tfq.getId()).getAutoAssignDistributionSet())
|
||||
.isNull();
|
||||
|
||||
mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/"+tfq.getId()+"/autoAssignDS"))
|
||||
.andExpect(status().isNoContent());
|
||||
|
||||
|
||||
}
|
||||
|
||||
private TargetFilterQuery createSingleTargetFilterQuery(final String name, final String query) {
|
||||
final TargetFilterQuery target = entityFactory.generateTargetFilterQuery();
|
||||
target.setName(name);
|
||||
target.setQuery(query);
|
||||
return targetFilterQueryManagement.createTargetFilterQuery(target);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* Rollout Management properties.
|
||||
*
|
||||
*/
|
||||
@ConfigurationProperties("hawkbit.autoassign")
|
||||
public class AutoAssignProperties {
|
||||
/**
|
||||
* Autoassign scheduler configuration.
|
||||
*/
|
||||
public static class Scheduler {
|
||||
// used by @Scheduled annotation which needs constant
|
||||
public static final String PROP_SCHEDULER_DELAY_PLACEHOLDER = "${hawkbit.autoassign.scheduler.fixedDelay:60000}";
|
||||
|
||||
/**
|
||||
* Schedule where the autoassign scheduler looks necessary state changes in
|
||||
* milliseconds.
|
||||
*/
|
||||
private long fixedDelay = 60000L;
|
||||
|
||||
public long getFixedDelay() {
|
||||
return fixedDelay;
|
||||
}
|
||||
|
||||
public void setFixedDelay(final long fixedDelay) {
|
||||
this.fixedDelay = fixedDelay;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final Scheduler scheduler = new Scheduler();
|
||||
|
||||
public Scheduler getScheduler() {
|
||||
return scheduler;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -104,6 +104,27 @@ public interface DeploymentManagement {
|
||||
DistributionSetAssignmentResult assignDistributionSet(@NotNull Long dsID,
|
||||
@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}
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()}
|
||||
|
||||
@@ -88,6 +88,11 @@ public interface DistributionSet extends NamedVersionedEntity {
|
||||
*/
|
||||
List<Target> getAssignedTargets();
|
||||
|
||||
/**
|
||||
* @return the auto assign target filters
|
||||
*/
|
||||
List<TargetFilterQuery> getAutoAssignFilters();
|
||||
|
||||
/**
|
||||
* @return the installedTargets
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.autoassign;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.hawkbit.exception.AbstractServerRtException;
|
||||
import org.eclipse.hawkbit.repository.DeploymentManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.RepositoryModelConstants;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||
import org.eclipse.hawkbit.repository.model.TargetWithActionType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.support.DefaultTransactionDefinition;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
/**
|
||||
* Checks if targets need a new distribution set (DS) based on the target filter
|
||||
* queries and assigns the new DS when necessary. First all target filter
|
||||
* queries are listed. For every target filter query (TFQ) the auto assign DS is
|
||||
* retrieved. All targets get listed per target filter query, that match the TFQ
|
||||
* and that don't have the auto assign DS in their action history.
|
||||
*/
|
||||
public class AutoAssignChecker {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(AutoAssignChecker.class);
|
||||
|
||||
private final TargetFilterQueryManagement targetFilterQueryManagement;
|
||||
|
||||
private final TargetManagement targetManagement;
|
||||
|
||||
private final DeploymentManagement deploymentManagement;
|
||||
|
||||
private final TransactionTemplate transactionTemplate;
|
||||
|
||||
/**
|
||||
* Maximum for target filter queries with auto assign DS Maximum for targets
|
||||
* that are fetched in one turn
|
||||
*/
|
||||
private static final int PAGE_SIZE = 1000;
|
||||
|
||||
/**
|
||||
* The message which is added to the action status when a distribution set
|
||||
* is assigned to an target. First %s is the name of the target filter.
|
||||
*/
|
||||
private static final String ACTION_MESSAGE = "Auto assignment by target filter: %s";
|
||||
|
||||
/**
|
||||
* Instantiates a new auto assign checker
|
||||
*
|
||||
* @param targetFilterQueryManagement
|
||||
* to get all target filter queries
|
||||
* @param targetManagement
|
||||
* to get targets
|
||||
* @param deploymentManagement
|
||||
* to assign distribution sets to targets
|
||||
* @param transactionManager
|
||||
* to run transactions
|
||||
*/
|
||||
public AutoAssignChecker(TargetFilterQueryManagement targetFilterQueryManagement, TargetManagement targetManagement,
|
||||
DeploymentManagement deploymentManagement, PlatformTransactionManager transactionManager) {
|
||||
this.targetFilterQueryManagement = targetFilterQueryManagement;
|
||||
this.targetManagement = targetManagement;
|
||||
this.deploymentManagement = deploymentManagement;
|
||||
|
||||
final DefaultTransactionDefinition def = new DefaultTransactionDefinition();
|
||||
def.setName("autoAssignDSToTargets");
|
||||
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
|
||||
transactionTemplate = new TransactionTemplate(transactionManager, def);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks all target filter queries with an auto assign distribution set and
|
||||
* triggers the check and assignment to targets that don't have the design
|
||||
* DS yet
|
||||
*/
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
public void check() {
|
||||
|
||||
PageRequest pageRequest = new PageRequest(0, PAGE_SIZE);
|
||||
|
||||
Page<TargetFilterQuery> filterQueries = targetFilterQueryManagement
|
||||
.findTargetFilterQueryWithAutoAssignDS(pageRequest);
|
||||
|
||||
for (TargetFilterQuery filterQuery : filterQueries) {
|
||||
checkByTargetFilterQueryAndAssignDS(filterQuery);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the distribution set, gets all controllerIds and assigns the DS
|
||||
* to them. Catches PersistenceException and own exceptions derived from
|
||||
* AbstractServerRtException
|
||||
*
|
||||
* @param targetFilterQuery
|
||||
* the target filter query
|
||||
*/
|
||||
private void checkByTargetFilterQueryAndAssignDS(TargetFilterQuery targetFilterQuery) {
|
||||
try {
|
||||
DistributionSet distributionSet = targetFilterQuery.getAutoAssignDistributionSet();
|
||||
|
||||
int count;
|
||||
do {
|
||||
|
||||
count = runTransactionalAssignment(targetFilterQuery, distributionSet.getId());
|
||||
|
||||
} while (count == PAGE_SIZE);
|
||||
|
||||
} catch (PersistenceException | AbstractServerRtException e) {
|
||||
LOGGER.error("Error during auto assign check of target filter query " + targetFilterQuery.getId(), e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs one page of target assignments within a dedicated transaction
|
||||
*
|
||||
* @param targetFilterQuery
|
||||
* the target filter query
|
||||
* @param dsId
|
||||
* distribution set id to assign
|
||||
* @return count of targets
|
||||
*/
|
||||
private int runTransactionalAssignment(TargetFilterQuery targetFilterQuery, Long dsId) {
|
||||
final String actionMessage = String.format(ACTION_MESSAGE, targetFilterQuery.getName());
|
||||
return transactionTemplate.execute(status -> {
|
||||
List<TargetWithActionType> targets = getTargetsWithActionType(targetFilterQuery, dsId, PAGE_SIZE);
|
||||
int count = targets.size();
|
||||
if (count > 0) {
|
||||
deploymentManagement.assignDistributionSet(dsId, targets, actionMessage);
|
||||
}
|
||||
return count;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all matching targets with the designated action from the target
|
||||
* management
|
||||
*
|
||||
* @param targetFilterQuery
|
||||
* the query the targets have to match
|
||||
* @param dsId
|
||||
* dsId the targets are not allowed to have in their action
|
||||
* history
|
||||
* @param count
|
||||
* maximum amount of targets to retrieve
|
||||
* @return list of targets with action type
|
||||
*/
|
||||
private List<TargetWithActionType> getTargetsWithActionType(TargetFilterQuery targetFilterQuery, Long dsId,
|
||||
int count) {
|
||||
Page<Target> targets = targetManagement.findAllTargetsByTargetFilterQueryAndNonDS(new PageRequest(0, count),
|
||||
dsId, targetFilterQuery);
|
||||
|
||||
return targets.getContent().stream().map(t -> new TargetWithActionType(t.getControllerId(),
|
||||
Action.ActionType.FORCED, RepositoryModelConstants.NO_FORCE_TIME)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.autoassign;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.hawkbit.repository.AutoAssignProperties;
|
||||
import org.eclipse.hawkbit.repository.SystemManagement;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
|
||||
/**
|
||||
* Scheduler to check target filters for auto assignment of distribution sets
|
||||
*/
|
||||
// don't active the auto assign scheduler in test, otherwise it is hard to test
|
||||
@Profile("!test")
|
||||
@EnableConfigurationProperties(AutoAssignProperties.class)
|
||||
public class AutoAssignScheduler {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(AutoAssignScheduler.class);
|
||||
|
||||
private final TenantAware tenantAware;
|
||||
|
||||
private final SystemManagement systemManagement;
|
||||
|
||||
private final SystemSecurityContext systemSecurityContext;
|
||||
|
||||
private final AutoAssignChecker autoAssignChecker;
|
||||
|
||||
/**
|
||||
* Instantiates a new AutoAssignScheduler
|
||||
*
|
||||
* @param tenantAware
|
||||
* to run as specific tenant
|
||||
* @param systemManagement
|
||||
* to find all tenants
|
||||
* @param systemSecurityContext
|
||||
* to run as system
|
||||
* @param autoAssignChecker
|
||||
* to run a check as tenant
|
||||
*/
|
||||
public AutoAssignScheduler(TenantAware tenantAware, SystemManagement systemManagement,
|
||||
SystemSecurityContext systemSecurityContext, AutoAssignChecker autoAssignChecker) {
|
||||
this.tenantAware = tenantAware;
|
||||
this.systemManagement = systemManagement;
|
||||
this.systemSecurityContext = systemSecurityContext;
|
||||
this.autoAssignChecker = autoAssignChecker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scheduler method called by the spring-async mechanism. Retrieves all
|
||||
* tenants from the {@link SystemManagement#findTenants()} and runs for each
|
||||
* tenant the auto assignments defined in the target filter queries
|
||||
* {@link SystemSecurityContext}.
|
||||
*/
|
||||
@Scheduled(initialDelayString = AutoAssignProperties.Scheduler.PROP_SCHEDULER_DELAY_PLACEHOLDER, fixedDelayString = AutoAssignProperties.Scheduler.PROP_SCHEDULER_DELAY_PLACEHOLDER)
|
||||
public void autoAssignScheduler() {
|
||||
LOGGER.debug("auto assign schedule checker has been triggered.");
|
||||
// run this code in system code privileged to have the necessary
|
||||
// permission to query and create entities.
|
||||
systemSecurityContext.runAsSystem(() -> {
|
||||
// workaround eclipselink that is currently not possible to
|
||||
// execute a query without multitenancy if MultiTenant
|
||||
// annotation is used.
|
||||
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=355458. So
|
||||
// iterate through all tenants and execute the rollout check for
|
||||
// each tenant separately.
|
||||
final List<String> tenants = systemManagement.findTenants();
|
||||
LOGGER.info("Checking target filter queries for tenants: {}", tenants.size());
|
||||
for (final String tenant : tenants) {
|
||||
tenantAware.runAsTenant(tenant, () -> {
|
||||
|
||||
autoAssignChecker.check();
|
||||
|
||||
return null;
|
||||
});
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,7 @@ import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
ALTER TABLE sp_target_filter_query
|
||||
ADD column auto_assign_distribution_set BIGINT;
|
||||
|
||||
ALTER TABLE sp_target_filter_query
|
||||
ADD CONSTRAINT fk_filter_auto_assign_ds
|
||||
FOREIGN KEY (auto_assign_distribution_set)
|
||||
REFERENCES sp_distribution_set
|
||||
ON DELETE SET NULL;
|
||||
@@ -0,0 +1,8 @@
|
||||
ALTER TABLE sp_target_filter_query
|
||||
ADD COLUMN auto_assign_distribution_set BIGINT;
|
||||
|
||||
ALTER TABLE sp_target_filter_query
|
||||
ADD CONSTRAINT fk_filter_auto_assign_ds
|
||||
FOREIGN KEY (auto_assign_distribution_set)
|
||||
REFERENCES sp_distribution_set (id)
|
||||
ON DELETE SET NULL;
|
||||
@@ -0,0 +1,257 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa;
|
||||
|
||||
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import ru.yandex.qatools.allure.annotations.Description;
|
||||
import ru.yandex.qatools.allure.annotations.Features;
|
||||
import ru.yandex.qatools.allure.annotations.Stories;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import static org.fest.assertions.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Test class for {@link TargetFilterQueryManagement}.
|
||||
*
|
||||
*/
|
||||
@Features("Component Tests - Repository")
|
||||
@Stories("Target Filter Query Management")
|
||||
public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
@Test
|
||||
@Description("Test creation of target filter query.")
|
||||
public void createTargetFilterQuery() {
|
||||
final String filterName = "new target filter";
|
||||
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
|
||||
.createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001"));
|
||||
assertEquals("Retrieved newly created custom target filter", targetFilterQuery,
|
||||
targetFilterQueryManagement.findTargetFilterQueryByName(filterName));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Test searching a target filter query.")
|
||||
public void searchTargetFilterQuery() {
|
||||
final String filterName = "targetFilterQueryName";
|
||||
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
|
||||
.createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001"));
|
||||
|
||||
targetFilterQueryManagement
|
||||
.createTargetFilterQuery(new JpaTargetFilterQuery("someOtherFilter", "name==PendingTargets002"));
|
||||
|
||||
List<TargetFilterQuery> results = targetFilterQueryManagement
|
||||
.findTargetFilterQueryByFilter(new PageRequest(0, 10), "name==" + filterName).getContent();
|
||||
assertEquals("Search result should have 1 result", 1, results.size());
|
||||
assertEquals("Retrieved newly created custom target filter", targetFilterQuery, results.get(0));
|
||||
}
|
||||
|
||||
@Test(expected = RSQLParameterUnsupportedFieldException.class)
|
||||
@Description("Test searching a target filter query with an invalid filter.")
|
||||
public void searchTargetFilterQueryInvalidField() {
|
||||
// Should throw an exception
|
||||
targetFilterQueryManagement.findTargetFilterQueryByFilter(new PageRequest(0, 10), "unknownField==testValue")
|
||||
.getContent();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Checks if the EntityAlreadyExistsException is thrown if a targetfilterquery with the same name are created more than once.")
|
||||
public void createDuplicateTargetFilterQuery() {
|
||||
final String filterName = "new target filter duplicate";
|
||||
targetFilterQueryManagement
|
||||
.createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001"));
|
||||
|
||||
try {
|
||||
targetFilterQueryManagement
|
||||
.createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001"));
|
||||
fail("should not have worked as query already exists");
|
||||
} catch (final EntityAlreadyExistsException e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Test deletion of target filter query.")
|
||||
public void deleteTargetFilterQuery() {
|
||||
final String filterName = "delete_target_filter_query";
|
||||
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
|
||||
.createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001"));
|
||||
targetFilterQueryManagement.deleteTargetFilterQuery(targetFilterQuery.getId());
|
||||
assertEquals("Returns null as the target filter is deleted", null,
|
||||
targetFilterQueryManagement.findTargetFilterQueryById(targetFilterQuery.getId()));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Test updation of target filter query.")
|
||||
public void updateTargetFilterQuery() {
|
||||
final String filterName = "target_filter_01";
|
||||
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
|
||||
.createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001"));
|
||||
|
||||
final String newQuery = "status==UNKNOWN";
|
||||
targetFilterQuery.setQuery(newQuery);
|
||||
targetFilterQueryManagement.updateTargetFilterQuery(targetFilterQuery);
|
||||
assertEquals("Returns updated target filter query", newQuery,
|
||||
targetFilterQueryManagement.findTargetFilterQueryByName(filterName).getQuery());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Test assigning a distribution set")
|
||||
public void assignDistributionSet() {
|
||||
final String filterName = "target_filter_02";
|
||||
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
|
||||
.createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001"));
|
||||
|
||||
final DistributionSet distributionSet = distributionSetManagement.createDistributionSet(new JpaDistributionSet(
|
||||
"dist_Set_01", "0.1", "", null, null
|
||||
));
|
||||
|
||||
targetFilterQuery.setAutoAssignDistributionSet(distributionSet);
|
||||
targetFilterQueryManagement.updateTargetFilterQuery(targetFilterQuery);
|
||||
|
||||
TargetFilterQuery tfq = targetFilterQueryManagement.findTargetFilterQueryByName(filterName);
|
||||
|
||||
assertEquals("Returns correct distribution set", distributionSet,
|
||||
tfq.getAutoAssignDistributionSet());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Test removing distribution set while it has a relation to a target filter query")
|
||||
public void removeAssignDistributionSet() {
|
||||
final String filterName = "target_filter_03";
|
||||
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
|
||||
.createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001"));
|
||||
|
||||
final DistributionSet distributionSet = distributionSetManagement.createDistributionSet(new JpaDistributionSet(
|
||||
"dist_Set_02", "0.1", "", null, null
|
||||
));
|
||||
|
||||
targetFilterQuery.setAutoAssignDistributionSet(distributionSet);
|
||||
targetFilterQueryManagement.updateTargetFilterQuery(targetFilterQuery);
|
||||
|
||||
// Check if target filter query is there
|
||||
TargetFilterQuery tfq = targetFilterQueryManagement.findTargetFilterQueryByName(filterName);
|
||||
assertEquals("Returns correct distribution set", distributionSet,
|
||||
tfq.getAutoAssignDistributionSet());
|
||||
|
||||
distributionSetManagement.deleteDistributionSet(distributionSet);
|
||||
|
||||
// Check if auto assign distribution set is null
|
||||
tfq = targetFilterQueryManagement.findTargetFilterQueryByName(filterName);
|
||||
assertNotNull("Returns target filter query", tfq);
|
||||
assertNull("Returns distribution set as null", tfq.getAutoAssignDistributionSet());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Test to implicitly remove the auto assign distribution set when the ds is soft deleted")
|
||||
public void implicitlyRemoveAssignDistributionSet() {
|
||||
final String filterName = "target_filter_03";
|
||||
DistributionSet distributionSet = testdataFactory.createDistributionSet("dist_set");
|
||||
Target target = testdataFactory.createTarget();
|
||||
|
||||
// Assign the distribution set to an target, to force a soft delete in a
|
||||
// later step
|
||||
deploymentManagement.assignDistributionSet(distributionSet.getId(), target.getControllerId());
|
||||
|
||||
targetFilterQueryManagement.createTargetFilterQuery(
|
||||
new JpaTargetFilterQuery(filterName, "name==PendingTargets001", (JpaDistributionSet) distributionSet));
|
||||
|
||||
// Check if target filter query is there with the distribution set
|
||||
TargetFilterQuery tfq = targetFilterQueryManagement.findTargetFilterQueryByName(filterName);
|
||||
assertEquals("Returns correct distribution set", distributionSet, tfq.getAutoAssignDistributionSet());
|
||||
|
||||
distributionSetManagement.deleteDistributionSet(distributionSet);
|
||||
|
||||
// Check if distribution set is still in the database with deleted flag
|
||||
assertTrue("Distribution set should be deleted",
|
||||
distributionSetManagement.findDistributionSetById(distributionSet.getId()).isDeleted());
|
||||
|
||||
// Check if auto assign distribution set is null
|
||||
tfq = targetFilterQueryManagement.findTargetFilterQueryByName(filterName);
|
||||
assertNotNull("Returns target filter query", tfq);
|
||||
assertNull("Returns distribution set as null", tfq.getAutoAssignDistributionSet());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Test finding and auto assign distribution set")
|
||||
public void findFiltersWithDistributionSet() {
|
||||
|
||||
final String filterName = "d";
|
||||
|
||||
assertEquals(0L, targetFilterQueryManagement.countAllTargetFilterQuery().longValue());
|
||||
|
||||
targetFilterQueryManagement.createTargetFilterQuery(new JpaTargetFilterQuery("a", "name==*"));
|
||||
targetFilterQueryManagement.createTargetFilterQuery(new JpaTargetFilterQuery("b", "name==*"));
|
||||
|
||||
final DistributionSet distributionSet = distributionSetManagement
|
||||
.createDistributionSet(new JpaDistributionSet("dist_Set_01", "0.1", "", null, null));
|
||||
final DistributionSet distributionSet2 = distributionSetManagement
|
||||
.createDistributionSet(new JpaDistributionSet("dist_Set_02", "0.1", "", null, null));
|
||||
|
||||
final TargetFilterQuery tfq = targetFilterQueryManagement.createTargetFilterQuery(
|
||||
new JpaTargetFilterQuery("c", "name==x", (JpaDistributionSet) distributionSet));
|
||||
|
||||
final TargetFilterQuery tfq2 = targetFilterQueryManagement.createTargetFilterQuery(
|
||||
new JpaTargetFilterQuery(filterName, "name==z*", (JpaDistributionSet) distributionSet2));
|
||||
|
||||
assertEquals(4L, targetFilterQueryManagement.countAllTargetFilterQuery().longValue());
|
||||
|
||||
// check if find works
|
||||
Page<TargetFilterQuery> tfqList = targetFilterQueryManagement
|
||||
.findTargetFilterQueryByAutoAssignDS(new PageRequest(0, 500), distributionSet);
|
||||
assertThat(1L).as("Target filter query").isEqualTo(tfqList.getTotalElements());
|
||||
|
||||
assertEquals("Returns correct target filter query", tfq.getId(), tfqList.iterator().next().getId());
|
||||
|
||||
tfq2.setAutoAssignDistributionSet(distributionSet);
|
||||
targetFilterQueryManagement.updateTargetFilterQuery(tfq2);
|
||||
|
||||
// check if find works for two
|
||||
tfqList = targetFilterQueryManagement.findTargetFilterQueryByAutoAssignDS(new PageRequest(0, 500),
|
||||
distributionSet);
|
||||
assertThat(2L).as("Target filter query count").isEqualTo(tfqList.getTotalElements());
|
||||
Iterator<TargetFilterQuery> iterator = tfqList.iterator();
|
||||
assertEquals("Returns correct target filter query 1", tfq.getId(), iterator.next().getId());
|
||||
assertEquals("Returns correct target filter query 2", tfq2.getId(), iterator.next().getId());
|
||||
|
||||
// check if find works with name filter
|
||||
tfqList = targetFilterQueryManagement.findTargetFilterQueryByAutoAssignDS(new PageRequest(0, 500),
|
||||
distributionSet, "name==" + filterName);
|
||||
assertThat(1L).as("Target filter query count").isEqualTo(tfqList.getTotalElements());
|
||||
|
||||
assertEquals("Returns correct target filter query", tfq2.getId(), tfqList.iterator().next().getId());
|
||||
|
||||
// check if find works for all with auto assign DS
|
||||
tfqList = targetFilterQueryManagement.findTargetFilterQueryWithAutoAssignDS(new PageRequest(0, 500));
|
||||
assertThat(2L).as("Target filter query count").isEqualTo(tfqList.getTotalElements());
|
||||
iterator = tfqList.iterator();
|
||||
assertEquals("Returns correct target filter query 1", tfq.getId(), iterator.next().getId());
|
||||
assertEquals("Returns correct target filter query 2", tfq2.getId(), iterator.next().getId());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery;
|
||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||
import org.junit.Test;
|
||||
|
||||
import ru.yandex.qatools.allure.annotations.Description;
|
||||
import ru.yandex.qatools.allure.annotations.Features;
|
||||
import ru.yandex.qatools.allure.annotations.Stories;
|
||||
|
||||
/**
|
||||
* Test class for {@link TargetFilterQueryManagement}.
|
||||
*
|
||||
*/
|
||||
@Features("Component Tests - Repository")
|
||||
@Stories("Target Filter Query Management")
|
||||
public class TargetFilterQueryManagenmentTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
@Test
|
||||
@Description("Test creation of target filter query.")
|
||||
public void createTargetFilterQuery() {
|
||||
final String filterName = "new target filter";
|
||||
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
|
||||
.createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001"));
|
||||
assertEquals("Retrieved newly created custom target filter", targetFilterQuery,
|
||||
targetFilterQueryManagement.findTargetFilterQueryByName(filterName));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Checks if the EntityAlreadyExistsException is thrown if a targetfilterquery with the same name are created more than once.")
|
||||
public void createDuplicateTargetFilterQuery() {
|
||||
final String filterName = "new target filter duplicate";
|
||||
targetFilterQueryManagement
|
||||
.createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001"));
|
||||
|
||||
try {
|
||||
targetFilterQueryManagement
|
||||
.createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001"));
|
||||
fail("should not have worked as query already exists");
|
||||
} catch (final EntityAlreadyExistsException e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Test deletion of target filter query.")
|
||||
public void deleteTargetFilterQuery() {
|
||||
final String filterName = "delete_target_filter_query";
|
||||
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
|
||||
.createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001"));
|
||||
targetFilterQueryManagement.deleteTargetFilterQuery(targetFilterQuery.getId());
|
||||
assertEquals("Returns null as the target filter is deleted", null,
|
||||
targetFilterQueryManagement.findTargetFilterQueryById(targetFilterQuery.getId()));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Test updation of target filter query.")
|
||||
public void updateTargetFilterQuery() {
|
||||
final String filterName = "target_filter_01";
|
||||
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
|
||||
.createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001"));
|
||||
|
||||
final String newQuery = "status==UNKNOWN";
|
||||
targetFilterQuery.setQuery(newQuery);
|
||||
targetFilterQueryManagement.updateTargetFilterQuery(targetFilterQuery);
|
||||
assertEquals("Returns updated target filter query", newQuery,
|
||||
targetFilterQueryManagement.findTargetFilterQueryByName(filterName).getQuery());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,15 +24,8 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.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() {
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.autoassign;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import ru.yandex.qatools.allure.annotations.Description;
|
||||
import ru.yandex.qatools.allure.annotations.Features;
|
||||
import ru.yandex.qatools.allure.annotations.Step;
|
||||
import ru.yandex.qatools.allure.annotations.Stories;
|
||||
|
||||
import static org.fest.assertions.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Test class for {@link AutoAssignChecker}.
|
||||
*
|
||||
*/
|
||||
@Features("Component Tests - Repository")
|
||||
@Stories("Auto assign checker")
|
||||
public class AutoAssignCheckerTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private AutoAssignChecker autoAssignChecker;
|
||||
|
||||
@Test
|
||||
@Description("Test auto assignment of a DS to filtered targets")
|
||||
public void checkAutoAssign() {
|
||||
|
||||
final DistributionSet setA = testdataFactory.createDistributionSet("dsA"); // will be auto assigned
|
||||
final DistributionSet setB = testdataFactory.createDistributionSet("dsB");
|
||||
|
||||
// target filter query that matches all targets
|
||||
TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
|
||||
.createTargetFilterQuery(new JpaTargetFilterQuery("filterA", "name==*"));
|
||||
targetFilterQuery.setAutoAssignDistributionSet(setA);
|
||||
targetFilterQueryManagement.updateTargetFilterQuery(targetFilterQuery);
|
||||
|
||||
|
||||
final String targetDsAIdPref = "targ";
|
||||
List<Target> targets = targetManagement.createTargets(
|
||||
testdataFactory.generateTargets(100, targetDsAIdPref, targetDsAIdPref.concat(" description")));
|
||||
int targetsCount = targets.size();
|
||||
|
||||
// assign set A to first 10 targets
|
||||
deploymentManagement.assignDistributionSet(setA, targets.subList(0, 10));
|
||||
verifyThatTargetsHaveDistributionSetAssignment(setA, targets.subList(0, 10), targetsCount);
|
||||
|
||||
// assign set B to first 5 targets
|
||||
// they have now 2 DS in their action history and should not get updated with dsA
|
||||
deploymentManagement.assignDistributionSet(setB, targets.subList(0, 5));
|
||||
verifyThatTargetsHaveDistributionSetAssignment(setB, targets.subList(0, 5), targetsCount);
|
||||
|
||||
// assign set B to next 10 targets
|
||||
deploymentManagement.assignDistributionSet(setB, targets.subList(10, 20));
|
||||
verifyThatTargetsHaveDistributionSetAssignment(setB, targets.subList(10, 20), targetsCount);
|
||||
|
||||
// Count the number of targets that will be assigned with setA
|
||||
assertThat(targetManagement.countTargetsByTargetFilterQueryAndNonDS(setA.getId(), targetFilterQuery))
|
||||
.isEqualTo(90);
|
||||
|
||||
// Run the check
|
||||
autoAssignChecker.check();
|
||||
|
||||
verifyThatTargetsHaveDistributionSetAssignment(setA, targets.subList(5, 100), targetsCount);
|
||||
|
||||
// first 5 should keep their dsB, because they already had the dsA once
|
||||
verifyThatTargetsHaveDistributionSetAssignment(setB, targets.subList(0, 5), targetsCount);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Test auto assignment of an incomplete DS to filtered targets, that causes failures")
|
||||
public void checkAutoAssignWithFailures() {
|
||||
|
||||
// incomplete distribution set that will be assigned
|
||||
final DistributionSet setF = distributionSetManagement
|
||||
.createDistributionSet(entityFactory.generateDistributionSet("dsA", "1", "incomplete ds",
|
||||
testdataFactory.findOrCreateDefaultTestDsType(), null));
|
||||
final DistributionSet setA = testdataFactory.createDistributionSet("dsA");
|
||||
final DistributionSet setB = testdataFactory.createDistributionSet("dsB");
|
||||
|
||||
final String targetDsAIdPref = "targA";
|
||||
final String targetDsFIdPref = "targB";
|
||||
|
||||
// target filter query that matches first bunch of targets, that should
|
||||
// fail
|
||||
targetFilterQueryManagement.createTargetFilterQuery(
|
||||
new JpaTargetFilterQuery("filterA", "id==" + targetDsFIdPref + "*", (JpaDistributionSet) setF));
|
||||
|
||||
// target filter query that matches failed bunch of targets
|
||||
targetFilterQueryManagement.createTargetFilterQuery(
|
||||
new JpaTargetFilterQuery("filterB", "id==" + targetDsAIdPref + "*", (JpaDistributionSet) setA));
|
||||
|
||||
List<Target> targetsF = targetManagement.createTargets(
|
||||
testdataFactory.generateTargets(10, targetDsFIdPref, targetDsFIdPref.concat(" description")));
|
||||
|
||||
List<Target> targetsA = targetManagement.createTargets(
|
||||
testdataFactory.generateTargets(10, targetDsAIdPref, targetDsAIdPref.concat(" description")));
|
||||
|
||||
int targetsCount = targetsA.size() + targetsF.size();
|
||||
|
||||
// assign set B to first 5 targets of fail group
|
||||
deploymentManagement.assignDistributionSet(setB, targetsF.subList(0, 5));
|
||||
verifyThatTargetsHaveDistributionSetAssignment(setB, targetsF.subList(0, 5), targetsCount);
|
||||
|
||||
// Run the check
|
||||
autoAssignChecker.check();
|
||||
|
||||
// first 5 targets of the fail group should still have setB
|
||||
verifyThatTargetsHaveDistributionSetAssignment(setB, targetsF.subList(0, 5), targetsCount);
|
||||
|
||||
// all targets of A group should have received setA
|
||||
verifyThatTargetsHaveDistributionSetAssignment(setA, targetsA, targetsCount);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param set the expected distribution set
|
||||
* @param targets the targets that should have it
|
||||
*/
|
||||
@Step
|
||||
private void verifyThatTargetsHaveDistributionSetAssignment(final DistributionSet set, List<Target> targets, int count) {
|
||||
List<Long> targetIds = targets.stream().map(Target::getId).collect(Collectors.toList());
|
||||
|
||||
Slice<Target> targetsAll = targetManagement.findTargetsAll(new PageRequest(0, 1000));
|
||||
assertThat(targetsAll).as("Count of targets").hasSize(count);
|
||||
|
||||
for (Target target : targetsAll) {
|
||||
if(targetIds.contains(target.getId())) {
|
||||
assertThat(target.getAssignedDistributionSet()).as("assigned DS").isEqualTo(set);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.specifications;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.data.jpa.domain.Specifications;
|
||||
import ru.yandex.qatools.allure.annotations.Description;
|
||||
import ru.yandex.qatools.allure.annotations.Features;
|
||||
import ru.yandex.qatools.allure.annotations.Stories;
|
||||
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Expression;
|
||||
import javax.persistence.criteria.Path;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.fest.assertions.Assertions.assertThat;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@Features("Unit Tests - Repository")
|
||||
@Stories("Specifications builder")
|
||||
public class SpecificationsBuilderTest {
|
||||
|
||||
@Test
|
||||
@Description("Test the combination of specs on an empty list which returns null")
|
||||
public void combineWithAndEmptyList() {
|
||||
List<Specification<Object>> specList = Collections.emptyList();
|
||||
assertThat(SpecificationsBuilder.combineWithAnd(specList)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Test the combination of specs on an immutable list with one entry")
|
||||
public void combineWithAndSingleImmutableList() {
|
||||
Specification<Object> spec = (root, query, cb) -> cb.equal(root.get("field1"), "testValue");
|
||||
List<Specification<Object>> specList = Collections.singletonList(spec);
|
||||
Specifications<Object> specifications = SpecificationsBuilder.combineWithAnd(specList);
|
||||
assertThat(specifications).as("Specifications").isNotNull();
|
||||
|
||||
// mocks to call toPredicate on specifications
|
||||
CriteriaBuilder criteriaBuilder = mock(CriteriaBuilder.class);
|
||||
final Path field1 = mock(Path.class);
|
||||
final Predicate equalPredicate = mock(Predicate.class);
|
||||
final CriteriaQuery<Object[]> query = mock(CriteriaQuery.class);
|
||||
final Root<Object> root = mock(Root.class);
|
||||
|
||||
when(criteriaBuilder.equal(any(Expression.class), anyString())).thenReturn(equalPredicate);
|
||||
when(root.get("field1")).thenReturn(field1);
|
||||
|
||||
Predicate predicate = specifications.toPredicate(root, query, criteriaBuilder);
|
||||
|
||||
assertThat(predicate).isEqualTo(equalPredicate);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@Description("Test the combination of specs on a list with multiple entries")
|
||||
public void combineWithAndList() {
|
||||
Specification<Object> spec1 = (root, query, cb) -> cb.equal(root.get("field1"), "testValue1");
|
||||
Specification<Object> spec2 = (root, query, cb) -> cb.equal(root.get("field2"), "testValue2");
|
||||
|
||||
List<Specification<Object>> specList = new ArrayList<>(2);
|
||||
specList.add(spec1);
|
||||
specList.add(spec2);
|
||||
|
||||
Specifications<Object> specifications = SpecificationsBuilder.combineWithAnd(specList);
|
||||
assertThat(specifications).as("Specifications").isNotNull();
|
||||
|
||||
// mocks to call toPredicate on specifications
|
||||
CriteriaBuilder criteriaBuilder = mock(CriteriaBuilder.class);
|
||||
final Path field1 = mock(Path.class);
|
||||
final Path field2 = mock(Path.class);
|
||||
final Predicate equalPredicate1 = mock(Predicate.class);
|
||||
final Predicate equalPredicate2 = mock(Predicate.class);
|
||||
final Predicate combinedPredicate = mock(Predicate.class);
|
||||
final CriteriaQuery<Object[]> query = mock(CriteriaQuery.class);
|
||||
final Root<Object> root = mock(Root.class);
|
||||
|
||||
when(criteriaBuilder.equal(any(Path.class), eq("testValue1"))).thenReturn(equalPredicate1);
|
||||
when(criteriaBuilder.equal(any(Path.class), eq("testValue2"))).thenReturn(equalPredicate2);
|
||||
when(criteriaBuilder.and(eq(equalPredicate1), eq(equalPredicate2))).thenReturn(combinedPredicate);
|
||||
when(root.get("field1")).thenReturn(field1);
|
||||
when(root.get("field2")).thenReturn(field2);
|
||||
|
||||
Predicate predicate = specifications.toPredicate(root, query, criteriaBuilder);
|
||||
|
||||
assertThat(predicate).as("Combined predicate").isEqualTo(combinedPredicate);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,7 +18,7 @@ import org.slf4j.LoggerFactory;
|
||||
/**
|
||||
* An abstraction for all controller based security. Check if the tenant
|
||||
* 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() {
|
||||
|
||||
@@ -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(";"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.security;
|
||||
|
||||
import static org.fest.assertions.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
|
||||
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken.FileResource;
|
||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||
import org.eclipse.hawkbit.repository.model.TenantConfigurationValue;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import ru.yandex.qatools.allure.annotations.Description;
|
||||
import ru.yandex.qatools.allure.annotations.Features;
|
||||
import ru.yandex.qatools.allure.annotations.Stories;
|
||||
|
||||
@Features("Unit Tests - Security")
|
||||
@Stories("Issuer hash based authentication")
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class ControllerPreAuthenticatedSecurityHeaderFilterTest {
|
||||
|
||||
private ControllerPreAuthenticatedSecurityHeaderFilter underTest;
|
||||
|
||||
@Mock
|
||||
private TenantConfigurationManagement tenantConfigurationManagementMock;
|
||||
|
||||
@Mock
|
||||
private TenantSecurityToken tenantSecurityTokenMock;
|
||||
|
||||
private final SecurityContextTenantAware tenantAware = new SecurityContextTenantAware();
|
||||
|
||||
private static final String CA_COMMON_NAME = "ca-cn";
|
||||
private static final String CA_COMMON_NAME_VALUE = "box1";
|
||||
|
||||
private static final String X_SSL_ISSUER_HASH_1 = "X-Ssl-Issuer-Hash-1";
|
||||
|
||||
private static final String SINGLE_HASH = "hash1";
|
||||
private static final String SECOND_HASH = "hash2";
|
||||
private static final String UNKNOWN_HASH = "unknown";
|
||||
|
||||
private static final String MULTI_HASH = "hash1;hash2;hash3";
|
||||
|
||||
private static final TenantConfigurationValue<String> CONFIG_VALUE_SINGLE_HASH = TenantConfigurationValue
|
||||
.<String> builder().value(SINGLE_HASH).build();
|
||||
|
||||
private static final TenantConfigurationValue<String> CONFIG_VALUE_MULTI_HASH = TenantConfigurationValue
|
||||
.<String> builder().value(MULTI_HASH).build();
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
underTest = new ControllerPreAuthenticatedSecurityHeaderFilter(CA_COMMON_NAME, "X-Ssl-Issuer-Hash-%d",
|
||||
tenantConfigurationManagementMock, tenantAware, new SystemSecurityContext(tenantAware));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests the filter for issuer hash based authentication with a single known hash")
|
||||
public void testIssuerHashBasedAuthenticationWithSingleKnownHash() {
|
||||
final TenantSecurityToken securityToken = prepareSecurityToken(SINGLE_HASH);
|
||||
// use single known hash
|
||||
when(tenantConfigurationManagementMock.getConfigurationValue(
|
||||
eq(TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_AUTHORITY_NAME), eq(String.class)))
|
||||
.thenReturn(CONFIG_VALUE_SINGLE_HASH);
|
||||
assertThat(underTest.getPreAuthenticatedPrincipal(securityToken)).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests the filter for issuer hash based authentication with multiple known hashes")
|
||||
public void testIssuerHashBasedAuthenticationWithMultipleKnownHashes() {
|
||||
final TenantSecurityToken securityToken = prepareSecurityToken(SINGLE_HASH);
|
||||
// use multiple known hashes
|
||||
when(tenantConfigurationManagementMock.getConfigurationValue(
|
||||
eq(TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_AUTHORITY_NAME), eq(String.class)))
|
||||
.thenReturn(CONFIG_VALUE_MULTI_HASH);
|
||||
assertThat(underTest.getPreAuthenticatedPrincipal(securityToken)).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests the filter for issuer hash based authentication with unknown hash")
|
||||
public void testIssuerHashBasedAuthenticationWithUnknownHash() {
|
||||
final TenantSecurityToken securityToken = prepareSecurityToken(UNKNOWN_HASH);
|
||||
// use single known hash
|
||||
when(tenantConfigurationManagementMock.getConfigurationValue(
|
||||
eq(TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_AUTHORITY_NAME), eq(String.class)))
|
||||
.thenReturn(CONFIG_VALUE_MULTI_HASH);
|
||||
assertThat(underTest.getPreAuthenticatedPrincipal(securityToken)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests different values for issuer hash header and inspects the credentials")
|
||||
public void useDifferentValuesForIssuerHashHeader() {
|
||||
final TenantSecurityToken securityToken1 = prepareSecurityToken(SINGLE_HASH);
|
||||
final TenantSecurityToken securityToken2 = prepareSecurityToken(SECOND_HASH);
|
||||
|
||||
final HeaderAuthentication expected1 = new HeaderAuthentication(CA_COMMON_NAME_VALUE, SINGLE_HASH);
|
||||
final HeaderAuthentication expected2 = new HeaderAuthentication(CA_COMMON_NAME_VALUE, SECOND_HASH);
|
||||
|
||||
when(tenantConfigurationManagementMock.getConfigurationValue(
|
||||
eq(TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_AUTHORITY_NAME), eq(String.class)))
|
||||
.thenReturn(CONFIG_VALUE_MULTI_HASH);
|
||||
|
||||
final Collection<HeaderAuthentication> credentials1 = (Collection<HeaderAuthentication>) underTest
|
||||
.getPreAuthenticatedCredentials(securityToken1);
|
||||
final Collection<HeaderAuthentication> credentials2 = (Collection<HeaderAuthentication>) underTest
|
||||
.getPreAuthenticatedCredentials(securityToken2);
|
||||
|
||||
final Object principal1 = underTest.getPreAuthenticatedPrincipal(securityToken1);
|
||||
final Object principal2 = underTest.getPreAuthenticatedPrincipal(securityToken2);
|
||||
|
||||
assertThat(credentials1.contains(expected1)).isTrue();
|
||||
assertThat(credentials2.contains(expected2)).isTrue();
|
||||
|
||||
assertEquals("hash1 expected in principal!", expected1, principal1);
|
||||
assertEquals("hash2 expected in principal!", expected2, principal2);
|
||||
|
||||
}
|
||||
|
||||
private static TenantSecurityToken prepareSecurityToken(final String issuerHashHeaderValue) {
|
||||
final TenantSecurityToken securityToken = new TenantSecurityToken("DEFAULT", CA_COMMON_NAME_VALUE,
|
||||
FileResource.createFileResourceBySha1("12345"));
|
||||
securityToken.putHeader(CA_COMMON_NAME, CA_COMMON_NAME_VALUE);
|
||||
securityToken.putHeader(X_SSL_ISSUER_HASH_1, issuerHashHeaderValue);
|
||||
return securityToken;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,9 +8,7 @@
|
||||
http://www.eclipse.org/legal/epl-v10.html
|
||||
|
||||
-->
|
||||
|
||||
<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>
|
||||
@@ -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));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.ui.common.detailslayout;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.vaadin.data.Container;
|
||||
import com.vaadin.data.Item;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||
|
||||
import com.vaadin.data.util.IndexedContainer;
|
||||
import com.vaadin.spring.annotation.SpringComponent;
|
||||
import com.vaadin.spring.annotation.VaadinSessionScope;
|
||||
import com.vaadin.ui.Table;
|
||||
import com.vaadin.ui.themes.ValoTheme;
|
||||
import org.eclipse.hawkbit.ui.utils.I18N;
|
||||
import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions;
|
||||
|
||||
/**
|
||||
*
|
||||
* DistributionSet TargetFilterQuery table
|
||||
*
|
||||
*/
|
||||
|
||||
@SpringComponent
|
||||
@VaadinSessionScope
|
||||
public class TargetFilterQueryDetailsTable extends Table {
|
||||
|
||||
private static final long serialVersionUID = 2913758299611837718L;
|
||||
|
||||
private static final String TFQ_NAME = "name";
|
||||
private static final String TFQ_QUERY = "query";
|
||||
|
||||
private I18N i18n;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param i18n
|
||||
*/
|
||||
public void init(final I18N i18n) {
|
||||
this.i18n = i18n;
|
||||
createTable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate software module metadata.
|
||||
*
|
||||
* @param distributionSet the selected distribution set
|
||||
*/
|
||||
public void populateTableByDistributionSet(final DistributionSet distributionSet) {
|
||||
removeAllItems();
|
||||
if (distributionSet == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Container dataSource = getContainerDataSource();
|
||||
List<TargetFilterQuery> filters = distributionSet.getAutoAssignFilters();
|
||||
filters.forEach(query -> {
|
||||
Object itemId = dataSource.addItem();
|
||||
Item item = dataSource.getItem(itemId);
|
||||
item.getItemProperty(TFQ_NAME).setValue(query.getName());
|
||||
item.getItemProperty(TFQ_QUERY).setValue(query.getQuery());
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private void createTable() {
|
||||
addStyleName(ValoTheme.TABLE_NO_HORIZONTAL_LINES);
|
||||
addStyleName(ValoTheme.TABLE_NO_STRIPES);
|
||||
addStyleName(SPUIStyleDefinitions.SW_MODULE_TABLE);
|
||||
addStyleName("details-layout");
|
||||
setSelectable(false);
|
||||
setImmediate(true);
|
||||
setContainerDataSource(getDistSetContainer());
|
||||
setColumnHeaderMode(ColumnHeaderMode.EXPLICIT);
|
||||
addTableHeader();
|
||||
setSizeFull();
|
||||
// same as height of other tabs in details tabsheet
|
||||
setHeight(116, Unit.PIXELS);
|
||||
}
|
||||
|
||||
private IndexedContainer getDistSetContainer() {
|
||||
final IndexedContainer container = new IndexedContainer();
|
||||
container.addContainerProperty(TFQ_NAME, String.class, "");
|
||||
container.addContainerProperty(TFQ_QUERY, String.class, "");
|
||||
setColumnExpandRatio(TFQ_NAME, 0.4F);
|
||||
setColumnAlignment(TFQ_NAME, Align.LEFT);
|
||||
setColumnExpandRatio(TFQ_QUERY, 0.6F);
|
||||
setColumnAlignment(TFQ_QUERY, Align.LEFT);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
private void addTableHeader() {
|
||||
setColumnHeader(TFQ_NAME, i18n.get("header.target.filter.name"));
|
||||
setColumnHeader(TFQ_QUERY, i18n.get("header.target.filter.query"));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -10,6 +10,9 @@ package org.eclipse.hawkbit.ui.components;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.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
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.ui.filtermanagement;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.PreDestroy;
|
||||
|
||||
import org.eclipse.hawkbit.repository.eventbus.event.DistributionCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.eventbus.event.DistributionDeletedEvent;
|
||||
import org.eclipse.hawkbit.ui.distributions.dstable.ManageDistBeanQuery;
|
||||
import org.eclipse.hawkbit.ui.distributions.state.ManageDistUIState;
|
||||
import org.eclipse.hawkbit.ui.utils.I18N;
|
||||
import org.eclipse.hawkbit.ui.utils.SPUIDefinitions;
|
||||
import org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions;
|
||||
import org.eclipse.hawkbit.ui.utils.TableColumn;
|
||||
import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.vaadin.addons.lazyquerycontainer.BeanQueryFactory;
|
||||
import org.vaadin.addons.lazyquerycontainer.LazyQueryContainer;
|
||||
import org.vaadin.addons.lazyquerycontainer.LazyQueryDefinition;
|
||||
import org.vaadin.spring.events.EventBus;
|
||||
import org.vaadin.spring.events.EventScope;
|
||||
import org.vaadin.spring.events.annotation.EventBusListenerMethod;
|
||||
|
||||
import com.vaadin.data.Container;
|
||||
import com.vaadin.spring.annotation.SpringComponent;
|
||||
import com.vaadin.spring.annotation.ViewScope;
|
||||
import com.vaadin.ui.Table;
|
||||
import com.vaadin.ui.themes.ValoTheme;
|
||||
|
||||
/**
|
||||
* Table for selecting a distribution set.
|
||||
*/
|
||||
@SpringComponent
|
||||
@ViewScope
|
||||
public class DistributionSetSelectTable extends Table {
|
||||
|
||||
private static final long serialVersionUID = -4307487829435471759L;
|
||||
|
||||
@Autowired
|
||||
private I18N i18n;
|
||||
|
||||
@Autowired
|
||||
private transient EventBus.SessionEventBus eventBus;
|
||||
|
||||
@Autowired
|
||||
private ManageDistUIState manageDistUIState;
|
||||
|
||||
private Container container;
|
||||
|
||||
/**
|
||||
* Initialize the component.
|
||||
*/
|
||||
@PostConstruct
|
||||
protected void init() {
|
||||
setStyleName("sp-table");
|
||||
setSizeFull();
|
||||
setSelectable(true);
|
||||
setMultiSelect(false);
|
||||
setImmediate(true);
|
||||
addStyleName(ValoTheme.TABLE_NO_VERTICAL_LINES);
|
||||
addStyleName(ValoTheme.TABLE_SMALL);
|
||||
populateTableData();
|
||||
setColumnCollapsingAllowed(false);
|
||||
setColumnProperties();
|
||||
setId(UIComponentIdProvider.DIST_SET_SELECT_TABLE_ID);
|
||||
eventBus.subscribe(this);
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
void destroy() {
|
||||
eventBus.unsubscribe(this);
|
||||
}
|
||||
|
||||
@EventBusListenerMethod(scope = EventScope.SESSION)
|
||||
void onEvents(final List<?> events) {
|
||||
final Object firstEvent = events.get(0);
|
||||
if (DistributionCreatedEvent.class.isInstance(firstEvent)
|
||||
|| DistributionDeletedEvent.class.isInstance(firstEvent)) {
|
||||
refreshDistributions();
|
||||
}
|
||||
}
|
||||
|
||||
private void populateTableData() {
|
||||
container = createContainer();
|
||||
addContainerproperties();
|
||||
setContainerDataSource(container);
|
||||
setColumnProperties();
|
||||
|
||||
}
|
||||
|
||||
protected Container createContainer() {
|
||||
|
||||
final Map<String, Object> queryConfiguration = prepareQueryConfigFilters();
|
||||
final BeanQueryFactory<ManageDistBeanQuery> distributionQF = new BeanQueryFactory<>(ManageDistBeanQuery.class);
|
||||
|
||||
distributionQF.setQueryConfiguration(queryConfiguration);
|
||||
return new LazyQueryContainer(
|
||||
new LazyQueryDefinition(true, SPUIDefinitions.PAGE_SIZE, SPUILabelDefinitions.VAR_DIST_ID_NAME),
|
||||
distributionQF);
|
||||
}
|
||||
|
||||
private void addContainerproperties() {
|
||||
/* Create HierarchicalContainer container */
|
||||
container.addContainerProperty(SPUILabelDefinitions.NAME, String.class, null);
|
||||
container.addContainerProperty(SPUILabelDefinitions.VAR_VERSION, String.class, null);
|
||||
}
|
||||
|
||||
private List<TableColumn> getVisbleColumns() {
|
||||
final List<TableColumn> columnList = new ArrayList<>(2);
|
||||
columnList.add(new TableColumn(SPUILabelDefinitions.NAME, i18n.get("header.name"), 0.6F));
|
||||
columnList.add(new TableColumn(SPUILabelDefinitions.VAR_VERSION, i18n.get("header.version"), 0.4F));
|
||||
return columnList;
|
||||
|
||||
}
|
||||
|
||||
private void setColumnProperties() {
|
||||
setVisibleColumns(getVisbleColumns().stream().map(column -> {
|
||||
setColumnHeader(column.getColumnPropertyId(), column.getColumnHeader());
|
||||
setColumnExpandRatio(column.getColumnPropertyId(), column.getExpandRatio());
|
||||
return column.getColumnPropertyId();
|
||||
}).toArray());
|
||||
}
|
||||
|
||||
private Map<String, Object> prepareQueryConfigFilters() {
|
||||
final Map<String, Object> queryConfig = new HashMap<>();
|
||||
manageDistUIState.getManageDistFilters().getSearchText()
|
||||
.ifPresent(value -> queryConfig.put(SPUIDefinitions.FILTER_BY_TEXT, value));
|
||||
|
||||
if (null != manageDistUIState.getManageDistFilters().getClickedDistSetType()) {
|
||||
queryConfig.put(SPUIDefinitions.FILTER_BY_DISTRIBUTION_SET_TYPE,
|
||||
manageDistUIState.getManageDistFilters().getClickedDistSetType());
|
||||
}
|
||||
|
||||
queryConfig.put(SPUIDefinitions.FILTER_BY_DS_COMPLETE, Boolean.TRUE);
|
||||
|
||||
return queryConfig;
|
||||
}
|
||||
|
||||
private void refreshDistributions() {
|
||||
final LazyQueryContainer dsContainer = (LazyQueryContainer) getContainerDataSource();
|
||||
final int size = dsContainer.size();
|
||||
if (size < SPUIDefinitions.MAX_TABLE_ENTRIES) {
|
||||
refreshTablecontainer();
|
||||
}
|
||||
if (size != 0) {
|
||||
setData(SPUIDefinitions.DATA_AVAILABLE);
|
||||
}
|
||||
}
|
||||
|
||||
private Object getItemIdToSelect() {
|
||||
if (manageDistUIState.getSelectedDistributions().isPresent()) {
|
||||
return manageDistUIState.getSelectedDistributions().get();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void selectRow() {
|
||||
setValue(getItemIdToSelect());
|
||||
}
|
||||
|
||||
private void refreshTablecontainer() {
|
||||
final LazyQueryContainer dsContainer = (LazyQueryContainer) getContainerDataSource();
|
||||
dsContainer.refresh();
|
||||
selectRow();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,307 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.ui.filtermanagement;
|
||||
|
||||
import com.vaadin.server.FontAwesome;
|
||||
import com.vaadin.ui.Alignment;
|
||||
import com.vaadin.ui.Button;
|
||||
import com.vaadin.ui.CheckBox;
|
||||
import com.vaadin.ui.HorizontalLayout;
|
||||
import com.vaadin.ui.Label;
|
||||
import com.vaadin.ui.UI;
|
||||
import com.vaadin.ui.VerticalLayout;
|
||||
import com.vaadin.ui.Window;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||
import org.eclipse.hawkbit.ui.common.CommonDialogWindow;
|
||||
import org.eclipse.hawkbit.ui.common.DistributionSetIdName;
|
||||
import org.eclipse.hawkbit.ui.common.builder.WindowBuilder;
|
||||
import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
|
||||
import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleNoBorderWithIcon;
|
||||
import org.eclipse.hawkbit.ui.filtermanagement.event.CustomFilterUIEvent;
|
||||
import org.eclipse.hawkbit.ui.utils.I18N;
|
||||
import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider;
|
||||
import org.eclipse.hawkbit.ui.utils.SPUIDefinitions;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.vaadin.spring.events.EventBus;
|
||||
|
||||
import com.vaadin.data.Property;
|
||||
import com.vaadin.server.Sizeable;
|
||||
import com.vaadin.spring.annotation.SpringComponent;
|
||||
import com.vaadin.spring.annotation.ViewScope;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Creates a dialog window to select the distribution set for a target filter query.
|
||||
*/
|
||||
@SpringComponent
|
||||
@ViewScope
|
||||
public class DistributionSetSelectWindow
|
||||
implements CommonDialogWindow.SaveDialogCloseListener, Property.ValueChangeListener {
|
||||
|
||||
private static final long serialVersionUID = 4752345414134989396L;
|
||||
|
||||
@Autowired
|
||||
private I18N i18n;
|
||||
|
||||
@Autowired
|
||||
private DistributionSetSelectTable dsTable;
|
||||
|
||||
@Autowired
|
||||
private transient EventBus.SessionEventBus eventBus;
|
||||
|
||||
@Autowired
|
||||
private transient DistributionSetManagement distributionSetManagement;
|
||||
|
||||
@Autowired
|
||||
private transient TargetManagement targetManagement;
|
||||
|
||||
@Autowired
|
||||
private transient TargetFilterQueryManagement targetFilterQueryManagement;
|
||||
|
||||
private CommonDialogWindow window;
|
||||
private CheckBox checkBox;
|
||||
private VerticalLayout verticalLayout;
|
||||
private Long tfqId;
|
||||
|
||||
private void init() {
|
||||
Label label = new Label(i18n.get("label.auto.assign.description"));
|
||||
|
||||
checkBox = new CheckBox(i18n.get("label.auto.assign.enable"));
|
||||
checkBox.setId(UIComponentIdProvider.DIST_SET_SELECT_ENABLE_ID);
|
||||
checkBox.setImmediate(true);
|
||||
checkBox.addValueChangeListener(this);
|
||||
|
||||
verticalLayout = new VerticalLayout();
|
||||
verticalLayout.addComponent(label);
|
||||
verticalLayout.addComponent(checkBox);
|
||||
verticalLayout.addComponent(dsTable);
|
||||
|
||||
window = new WindowBuilder(SPUIDefinitions.CREATE_UPDATE_WINDOW)
|
||||
.caption(i18n.get("caption.select.auto.assign.dist")).content(verticalLayout).layout(verticalLayout)
|
||||
.i18n(i18n).saveDialogCloseListener(this).buildCommonDialogWindow();
|
||||
window.setId(UIComponentIdProvider.DIST_SET_SELECT_WINDOW_ID);
|
||||
}
|
||||
|
||||
public void setValue(DistributionSetIdName distSet) {
|
||||
dsTable.setVisible(distSet != null);
|
||||
checkBox.setValue(distSet != null);
|
||||
dsTable.setValue(distSet);
|
||||
dsTable.setCurrentPageFirstItemId(distSet);
|
||||
}
|
||||
|
||||
public DistributionSetIdName getValue() {
|
||||
if (checkBox.getValue()) {
|
||||
return (DistributionSetIdName) dsTable.getValue();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a distribution set select window for the given target filter query
|
||||
*
|
||||
* @param tfqId
|
||||
* target filter query id
|
||||
*/
|
||||
public void showForTargetFilter(Long tfqId) {
|
||||
this.tfqId = tfqId;
|
||||
TargetFilterQuery tfq = targetFilterQueryManagement.findTargetFilterQueryById(tfqId);
|
||||
if(tfq == null) {
|
||||
throw new IllegalStateException("TargetFilterQuery does not exist for the given id");
|
||||
}
|
||||
|
||||
init();
|
||||
|
||||
DistributionSet distributionSet = tfq.getAutoAssignDistributionSet();
|
||||
if(distributionSet != null) {
|
||||
setValue(DistributionSetIdName.generate(distributionSet));
|
||||
} else {
|
||||
setValue(null);
|
||||
}
|
||||
|
||||
window.setWidth(40.0F, Sizeable.Unit.PERCENTAGE);
|
||||
UI.getCurrent().addWindow(window);
|
||||
window.setVisible(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is triggered when the checkbox value changes
|
||||
*
|
||||
* @param event
|
||||
* change event
|
||||
*/
|
||||
@Override
|
||||
public void valueChange(Property.ValueChangeEvent event) {
|
||||
dsTable.setVisible(checkBox.getValue());
|
||||
if (window != null) {
|
||||
window.center();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is triggered when the save button is clicked
|
||||
*
|
||||
* @return whether the click should be allowed
|
||||
*/
|
||||
@Override
|
||||
public boolean canWindowSaveOrUpdate() {
|
||||
return !checkBox.getValue() || dsTable.getValue() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is called when the new value should be saved after the save button has
|
||||
* been clicked
|
||||
*/
|
||||
@Override
|
||||
public void saveOrUpdate() {
|
||||
if(checkBox.getValue() && dsTable.getValue() != null) {
|
||||
DistributionSetIdName ds = (DistributionSetIdName) dsTable.getValue();
|
||||
updateTargetFilterQueryDS(tfqId, ds.getId());
|
||||
|
||||
} else if(!checkBox.getValue()) {
|
||||
updateTargetFilterQueryDS(tfqId, null);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void updateTargetFilterQueryDS(final Long targetFilterQueryId, final Long dsId) {
|
||||
TargetFilterQuery tfq = targetFilterQueryManagement.findTargetFilterQueryById(targetFilterQueryId);
|
||||
|
||||
|
||||
|
||||
if(dsId != null) {
|
||||
confirmWithConsequencesDialog(tfq, dsId);
|
||||
} else {
|
||||
tfq.setAutoAssignDistributionSet(null);
|
||||
targetFilterQueryManagement.updateTargetFilterQuery(tfq);
|
||||
eventBus.publish(this, CustomFilterUIEvent.UPDATED_TARGET_FILTER_QUERY);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void confirmWithConsequencesDialog(TargetFilterQuery tfq, final Long dsId) {
|
||||
|
||||
ConfirmConsequencesDialog dialog = new ConfirmConsequencesDialog(tfq, dsId, new ConfirmCallback() {
|
||||
@Override
|
||||
public void onConfirmResult(boolean accepted) {
|
||||
if(accepted) {
|
||||
tfq.setAutoAssignDistributionSet(distributionSetManagement.findDistributionSetById(dsId));
|
||||
targetFilterQueryManagement.updateTargetFilterQuery(tfq);
|
||||
eventBus.publish(this, CustomFilterUIEvent.UPDATED_TARGET_FILTER_QUERY);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
dialog.setWidth(40.0F, Sizeable.Unit.PERCENTAGE);
|
||||
|
||||
UI.getCurrent().addWindow(dialog);
|
||||
dialog.setVisible(true);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A dialog that displays how many targets will be assigned immediately with the
|
||||
*/
|
||||
private class ConfirmConsequencesDialog extends Window implements Button.ClickListener {
|
||||
|
||||
private static final long serialVersionUID = 7738545414137389326L;
|
||||
|
||||
private TargetFilterQuery targetFilterQuery;
|
||||
private Long distributionSetId;
|
||||
|
||||
private Button okButton;
|
||||
private Button cancelButton;
|
||||
|
||||
private ConfirmCallback callback;
|
||||
|
||||
public ConfirmConsequencesDialog(TargetFilterQuery targetFilterQuery, final Long dsId, ConfirmCallback callback) {
|
||||
super(i18n.get("caption.confirm.assign.consequences"));
|
||||
|
||||
this.callback = callback;
|
||||
this.targetFilterQuery = targetFilterQuery;
|
||||
this.distributionSetId = dsId;
|
||||
|
||||
init();
|
||||
|
||||
}
|
||||
|
||||
private void init() {
|
||||
setId(UIComponentIdProvider.DIST_SET_SELECT_CONS_WINDOW_ID);
|
||||
setModal(true);
|
||||
setResizable(false);
|
||||
|
||||
VerticalLayout layout = new VerticalLayout();
|
||||
layout.setSpacing(true);
|
||||
layout.setMargin(true);
|
||||
setContent(layout);
|
||||
|
||||
Long targetsCount = targetManagement.countTargetsByTargetFilterQueryAndNonDS(distributionSetId, targetFilterQuery);
|
||||
Label mainTextLabel;
|
||||
if(targetsCount == 0) {
|
||||
mainTextLabel = new Label(i18n.get("message.confirm.assign.consequences.none"));
|
||||
} else {
|
||||
mainTextLabel = new Label(i18n.get("message.confirm.assign.consequences.text", new Object[]{targetsCount}));
|
||||
}
|
||||
|
||||
layout.addComponent(mainTextLabel);
|
||||
|
||||
HorizontalLayout buttonsLayout = new HorizontalLayout();
|
||||
buttonsLayout.setSizeFull();
|
||||
buttonsLayout.setSpacing(true);
|
||||
buttonsLayout.addStyleName("actionButtonsMargin");
|
||||
layout.addComponent(buttonsLayout);
|
||||
|
||||
|
||||
okButton = SPUIComponentProvider.getButton(UIComponentIdProvider.SAVE_BUTTON, i18n.get("button.ok"), "", "", true,
|
||||
FontAwesome.SAVE, SPUIButtonStyleNoBorderWithIcon.class);
|
||||
okButton.setSizeUndefined();
|
||||
okButton.addStyleName("default-color");
|
||||
okButton.addClickListener(this);
|
||||
buttonsLayout.addComponent(okButton);
|
||||
buttonsLayout.setComponentAlignment(okButton, Alignment.MIDDLE_RIGHT);
|
||||
buttonsLayout.setExpandRatio(okButton, 1.0F);
|
||||
|
||||
|
||||
cancelButton = SPUIComponentProvider.getButton(UIComponentIdProvider.CANCEL_BUTTON, i18n.get("button.cancel"), "", "", true,
|
||||
FontAwesome.TIMES, SPUIButtonStyleNoBorderWithIcon.class);
|
||||
cancelButton.setSizeUndefined();
|
||||
cancelButton.addStyleName("default-color");
|
||||
cancelButton.addClickListener(this);
|
||||
buttonsLayout.addComponent(cancelButton);
|
||||
buttonsLayout.setComponentAlignment(cancelButton, Alignment.MIDDLE_LEFT);
|
||||
buttonsLayout.setExpandRatio(cancelButton, 1.0F);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buttonClick(Button.ClickEvent event) {
|
||||
if(event.getButton().getId().equals(okButton.getId())) {
|
||||
callback.onConfirmResult(true);
|
||||
} else {
|
||||
callback.onConfirmResult(false);
|
||||
}
|
||||
|
||||
close();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface ConfirmCallback extends Serializable {
|
||||
void onConfirmResult(boolean accepted);
|
||||
}
|
||||
}
|
||||
@@ -13,8 +13,10 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import 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();
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -168,6 +168,7 @@
|
||||
|
||||
.v-table-cell-content {
|
||||
height: 27px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -1,506 +0,0 @@
|
||||
#
|
||||
# Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
#
|
||||
# All rights reserved. This program and the accompanying materials
|
||||
# are made available under the terms of the Eclipse Public License v1.0
|
||||
# which accompanies this distribution, and is available at
|
||||
# http://www.eclipse.org/legal/epl-v10.html
|
||||
#
|
||||
|
||||
#########################################################################################
|
||||
# This is the messages_en.properties file
|
||||
#########################################################################################
|
||||
|
||||
# Button names prefix with - button
|
||||
button.save = Save
|
||||
button.delete = Delete
|
||||
button.discard = Discard
|
||||
button.discard.all = Discard All
|
||||
button.delete.all = Delete All
|
||||
button.assign.all = Save Assign
|
||||
button.actions = You have actions
|
||||
button.no.actions = No actions
|
||||
button.ok = OK
|
||||
button.cancel = Cancel
|
||||
button.upload.file = Upload File
|
||||
bulk.targets.upload = Please upload csv file.
|
||||
bulkupload.ds.name = DS Name
|
||||
|
||||
# Headers prefix with - header
|
||||
header.target.table=Targets
|
||||
header.dist.table=Distributions
|
||||
header.filter.tag=Filter by Tag
|
||||
header.target.filter.tag=Filters
|
||||
header.first.assignment.table = Targets
|
||||
header.second.assignment.table = Distributions
|
||||
header.dist.first.assignment.table = Distributions
|
||||
header.dist.second.assignment.table = Software Modules
|
||||
header.third.assignment.table = Discard
|
||||
header.one.deletedist.table = Distribution Name
|
||||
header.second.deletedist.table = Discard Changes
|
||||
header.first.deletetarget.table = Target Name
|
||||
header.second.deletetarget.table = Discard Changes
|
||||
header.first.deleteswmodule.table = Delete software
|
||||
header.first.delete.dist.type.table = DistributionSetType
|
||||
header.second.delete.dist.type.table = Discard
|
||||
header.first.delete.swmodule.type.table = Software Module Type
|
||||
header.second.delete.swmodule.type.table = Discard
|
||||
header.dist.twintable.selected=Selected
|
||||
header.dist.twintable.available=Available
|
||||
header.target.installed = Installed
|
||||
header.target.assigned = Assigned
|
||||
|
||||
# Captions prefix with - caption
|
||||
caption.action.history = Action history for {0}
|
||||
caption.error = Error
|
||||
caption.new.softwaremodule.application = Configure New Application
|
||||
caption.new.softwaremodule.jvm = Configure New Runtime
|
||||
caption.new.softwaremodule.os = Configure New OS
|
||||
caption.metadata = Metadata
|
||||
|
||||
|
||||
caption.add.softwaremodule = Configure Software Module
|
||||
caption.add.new.dist = Configure New Distribution
|
||||
caption.update.dist = Configure Update Distribution
|
||||
caption.add.tag = Configure Tag
|
||||
caption.add.type = Configure Type
|
||||
caption.add.new.target = Configure New Target
|
||||
caption.update.target = Configure Update Target
|
||||
caption.bulk.upload.targets = Bulk Upload
|
||||
caption.softwares.distdetail.tab = Modules
|
||||
caption.tags.tab = Tags
|
||||
caption.logs.tab = Logs
|
||||
caption.attributes.tab = Attributes
|
||||
caption.types.tab = Types
|
||||
caption.save.window = Action Details
|
||||
caption.assign.dist.accordion.tab = Assign Distribution Set
|
||||
caption.delete.dist.accordion.tab = Delete Distributions
|
||||
caption.delete.target.accordion.tab = Delete Targets
|
||||
caption.delete.swmodule.accordion.tab = Delete SW Modules
|
||||
caption.delete.dist.set.type.accordion.tab = Delete Distribution Set Type
|
||||
caption.delete.sw.module.type.accordion.tab = Delete Software Module Type
|
||||
caption.attributes = Attributes
|
||||
caption.panel.dist.installed = Installed Distribution Set
|
||||
caption.panel.dist.assigned = Assigned Distribution Set
|
||||
caption.soft.delete.confirmbox = Confirm Software Module Delete Action
|
||||
caption.cancel.action.confirmbox = Confirm Action Cancellation
|
||||
caption.forced.datefield = Force update at time
|
||||
caption.force.action.confirmbox = Confirm Force Active Action
|
||||
caption.filter.simple = Simple Filter
|
||||
caption.filter.custom = Custom Filter
|
||||
caption.filter.delete.confirmbox = Confirm Filter Delete Action
|
||||
caption.confirm.abort.action = Confirm Abort Action
|
||||
|
||||
caption.metadata.popup = Metadata of
|
||||
caption.metadata.delete.action.confirmbox = Confirm Metadata Delete Action
|
||||
|
||||
# Labels prefix with - label
|
||||
label.dist.details.type = Type :
|
||||
label.dist.details.name = Name :
|
||||
label.dist.details.version = Version :
|
||||
label.dist.details.vendor = Vendor :
|
||||
label.dist.details.jvm = Runtime :
|
||||
label.dist.details.ah = Application :
|
||||
label.dist.details.os = OS :
|
||||
label.modified.date = Last modified at :
|
||||
label.modified.by = Last modified by :
|
||||
label.created.at = Created at :
|
||||
label.created.by = Created by :
|
||||
label.target.count = Targets :
|
||||
label.description = Description :
|
||||
label.ip = Address :
|
||||
label.type = Type :
|
||||
label.assigned.type = Assignment type :
|
||||
label.assigned.count = {0} Assigned
|
||||
label.installed.count = {0} Installed
|
||||
label.mandatory.field = * Mandatory Field
|
||||
label.components.drop.area = Drop here to delete
|
||||
label.software.module.drop.area = Delete Software
|
||||
label.create.tag = Create Tag
|
||||
label.update.tag = Update Tag
|
||||
label.create.type = Create Type
|
||||
label.update.type = Update Type
|
||||
label.singleAssign.type = Firmware (FW)
|
||||
label.multiAssign.type = Software (SW)
|
||||
label.choose.type = Choose Type
|
||||
label.choose.type.color = Choose Type Color
|
||||
label.combobox.type = Select Type
|
||||
label.combobox.tag = Select Tag
|
||||
label.choose.tag = Choose Tag to update
|
||||
label.choose.tag.color = Choose Tag Color
|
||||
label.target.filtered.total = Total filtered targets :
|
||||
label.filter.selected = Selected:
|
||||
label.filter.shown = Shown:
|
||||
label.filter = Filter :
|
||||
label.target.filter.count = Total Targets :
|
||||
label.filter.selected = Selected :
|
||||
label.filter.shown = Shown :
|
||||
label.filter.status = Status,
|
||||
label.filter.overdue = Overdue,
|
||||
label.filter.tags = Tags,
|
||||
label.filter.text = Search Text
|
||||
label.filter.dist = Distribution,
|
||||
label.filter.custom = Custom
|
||||
label.target.filter.truncated={0} targets has been truncated in the list due the target size limit of {1}, use filters to reduce the targets to be shown
|
||||
label.active =Active
|
||||
label.inactive = In-active
|
||||
label.finished = Finished
|
||||
label.error = Error
|
||||
label.warning = Warning
|
||||
label.running = Running
|
||||
label.cancelled = Cancelled
|
||||
label.cancelling = Canceling
|
||||
label.retrieved = Retrieved
|
||||
label.download = Downloading
|
||||
label.scheduled = Scheduled
|
||||
label.target.id = Controller Id :
|
||||
label.target.ip = Controller IP :
|
||||
label.target.security.token = Security token :
|
||||
label.filter.by.status = Filter by Status
|
||||
label.filter.by.overdue = Filter by Overdue
|
||||
label.target.controller.attrs = <b>Controller attributes</b>
|
||||
label.target.lastpolldate = Last poll :
|
||||
label.no.tag.assigned = NO TAG
|
||||
label.tag.name = Tag name
|
||||
label.configuration.auth.header = Allow targets to authenticate via a certificate authenticated by an reverse proxy
|
||||
label.configuration.auth.gatewaytoken = Allow a gateway to authenticate and manage multiple targets through a gateway security token
|
||||
label.configuration.auth.targettoken = Allow targets to authenticate directly with their target security token
|
||||
label.configuration.anonymous.download = Allow targets to download artifacts without security credentials
|
||||
label.unsupported.browser.ie=Sorry! current browser is not supported. Please use Internet Explorer 11 and above
|
||||
|
||||
# Checkbox label prefix with - checkbox
|
||||
checkbox.dist.migration.required = Required Migration Step :
|
||||
checkbox.dist.required.migration.step = Required Migration Step
|
||||
|
||||
# TextFields prefix with - textfield
|
||||
textfield.name = Name
|
||||
textfield.key = Key
|
||||
textfield.version = Version
|
||||
textfield.vendor = Vendor
|
||||
textfield.description = Description
|
||||
textfield.customfiltername = Filter name
|
||||
textfield.value = Value
|
||||
ui.version = Powered by Bosch IoT Software Provisioning
|
||||
prompt.target.id = Controller ID
|
||||
|
||||
|
||||
#Tooltips prefix with - tooltip
|
||||
tooltip.add.module = Add Software Module
|
||||
tooltip.status.unknown = Unknown
|
||||
tooltip.status.registered = Registered
|
||||
tooltip.status.pending = Pending
|
||||
tooltip.status.error = Error
|
||||
tooltip.status.insync = In-sync
|
||||
tooltip.status.overdue = Overdue
|
||||
tooltip.delete.module = Select and delete Software Module
|
||||
tooltip.forced.item=Forced update action
|
||||
tooltip.soft.item=Soft update action which interacts with an user as a attempt update action for example
|
||||
tooltip.timeforced.item=Soft update until a specific time and then the action will be forced
|
||||
tooltip.check.for.mandatory=Check to make Mandatory
|
||||
tooltip.artifact.icon=Show Artifact Details
|
||||
tooltip.click.to.edit = Click to edit
|
||||
tooltip.metadata.icon = Manage Metadata
|
||||
|
||||
|
||||
# Notification messages prefix with - message
|
||||
message.save.success = {0} saved successfully
|
||||
message.update.success = {0} updated successfully
|
||||
message.delete.success = {0} deleted successfully
|
||||
message.dist.installedorassigned = Target {targId} is already assigned/installed with distribution
|
||||
message.dist.pending.action = Target {0} is already assigned with distribution {1} . Pending for action
|
||||
message.empty.target.tags= No Tags Created
|
||||
message.empty.disttype.tags = No Distribution type tags created
|
||||
message.select.row = Please select a row to drag
|
||||
message.error = Unknown error occured during the operation. Please contact administrator
|
||||
message.dist.assigned.one = {0} is assigned to {1}
|
||||
message.dist.assigned.many = {0} DistributionSets are assigned to {1}
|
||||
message.dist.unassigned.one = {0} is unassigned from {1}
|
||||
message.dist.unassigned.many = {0} DistributionSets are unassigned from {1}
|
||||
message.target.assigned.one = {0} is assigned to {1}
|
||||
message.target.assigned.many = {0} Targets are assigned to {1}
|
||||
message.target.unassigned.one = {0} is unassigned from {1}
|
||||
message.target.unassigned.many = {0} Targets are unassigned from {1}
|
||||
message.target.assigned.pending = Some target(s) are already assigned.Pending for action
|
||||
message.cannot.delete = Cannot be deleted
|
||||
message.check.softwaremodule = Please provide both name and version!
|
||||
message.cannot.delete.default.dstype = Default distribution set type cannot be deleted
|
||||
message.duplicate.softwaremodule = {0} : {1} already exists!
|
||||
message.cannot.delete.default.dstype = Default distribution set type cannot be deleted
|
||||
message.tag.delete = Please unclick the tag {0}, then try to delete
|
||||
message.dist.type.check.delete = Please unclick the distribution type {0}, then try to delete
|
||||
message.swmodule.type.check.delete = Please unclick the Software Module type {0}, then try to delete
|
||||
message.targets.already.deleted = Few Target(s) are already deleted.Pending for action
|
||||
message.dists.already.deleted = Few distribution(s) are already deleted.Pending for action
|
||||
message.target.deleted.pending = Target(s) already deleted.Pending for action
|
||||
message.dist.deleted.pending = Distribution(s) already deleted.Pending for action
|
||||
message.dist.delete.success = All selected distribution sets are deleted successfully !
|
||||
message.dist.discard.success = All distributions selected for delete are discarded successfully !
|
||||
message.target.delete.success = All selected targets are deleted successfully !
|
||||
message.target.discard.success = All targets selected for delete are discarded successfully !
|
||||
message.software.discard.success = All software modules selected for delete are discarded successfully !
|
||||
message.software.type.discard.success = All software moduleTypes selected for delete are discarded successfully !
|
||||
message.assign.software.discard.success = All software moduleTypes selected for assign are discarded successfully !
|
||||
message.software.delete.success = All software modules selected for delete are deleted successfully !
|
||||
message.software.type.delete.success = All software modules types selected for delete are deleted successfully !
|
||||
message.dist.set.type.deleted.success = {0} DistributionSetType deleted successfully !
|
||||
message.new.dist.save.success = {0} - {1} saved successfully
|
||||
message.dist.update.success = {0} - {1} updated successfully
|
||||
message.duplicate.dist = Distribution set [{0}] or version [{1}] must be unique, entered value already exists.
|
||||
message.error.view = No such view: {0}
|
||||
message.accessdenied.view = No access to view: {0}
|
||||
message.no.data = No data available
|
||||
message.target.assignment = {0} Assignment(s) done
|
||||
message.target.deleted = {0} Target(s) deleted
|
||||
message.dist.deleted = {0} Distribution Set(s) deleted
|
||||
message.tag.update.mandatory = Please select the Tag to update
|
||||
message.tag.duplicate.check = {0} already exists, please enter another value
|
||||
message.type.key.duplicate.check = Distribution type with key {0} already exists, please give another value
|
||||
message.type.key.swmodule.duplicate.check = Software Module type with key {0} already exists, please give another value
|
||||
message.no.action.history = No action history is available for the target : {0}
|
||||
message.no.available = --No messages available--
|
||||
message.no.actionupdateds.available = No other updates available for this action
|
||||
message.mandatory.check = Mandatory details are missing
|
||||
message.target.duplicate.check = Target [ {0} ] must be unique, entered value already exists.
|
||||
message.permission.insufficient = Insufficient permissions to perform this action.
|
||||
message.error.temp = The operation cannot be fulfilled due to {0}. Please contact administrator
|
||||
message.dist.alreadyassigned = {0} : {1} is already assigned/installed, cannot be updated
|
||||
message.dist.tag.alreadyassigned = {0} : {1} is already assigned/installed, cannot assign/un-assign to tag
|
||||
message.dists.unassign.tag.alreadyassigned = Few of the DistributionSet's are already assigned to Target, those cannot be unassigned from Tag"
|
||||
message.dists.assign.tag.alreadyassigned = Few of the DistributionSet's are already assigned to Target, those cannot be assigned to Tag"
|
||||
message.dists.tag.assigned = {0} DistributionSet's assigned to Tag {1}
|
||||
message.dists.tag.unassigned = {0} DistributionSet's un-assigned from Tag {1}
|
||||
message.dist.no.operation = {0} - already assigned/installed, No operation
|
||||
message.sm.delete.confirm = Are you sure that you want to delete the selected {0} Software Module?
|
||||
message.error.os.softmodule = Please select the OS to delete
|
||||
message.error.ah.softmodule = Please select the Application to delete
|
||||
message.error.softmodule.deleted = The selected Software Module is already deleted
|
||||
message.cancel.action = Cancel
|
||||
message.cancel.action.success = Action cancelled successfully !
|
||||
message.cancel.action.failed = Unable to cancel the action !
|
||||
message.cancel.action.confirm = Are you sure that you want to cancel this action?
|
||||
message.target.alreadyAssigned = {0} Target(s) were already assigned
|
||||
message.dist.alreadyAssigned = {0} Distribution Set(s) were already assigned
|
||||
message.force.action = Force
|
||||
message.force.action.confirm = Are you sure that you want to force this action?
|
||||
message.force.action.success = Action forced successfully !
|
||||
message.distribution.no.update = distribution {0} set is already assigned to targets and cannot be changed
|
||||
message.action.not.allowed = Action not allowed
|
||||
message.action.did.not.work = Action did not work. Please try again.
|
||||
message.onlyone.distribution.assigned = Only one distribution set can be assigned
|
||||
message.onlyone.distribution.dropallowed = Only one distribution set can be dropped
|
||||
message.error.missing.typename = Missing Type Name
|
||||
message.error.missing.typenameorkey = Missing Type Name or Key
|
||||
message.tag.cannot.be.assigned = Target/DS cannot be assigned to {0}
|
||||
message.no.targets.assiged.fortag = No targets are assigned to tag {0}
|
||||
message.error.missing.tagname = Please select tag name
|
||||
message.type.delete = Please unclick the distribution type {0}, then try to delete
|
||||
message.error.dist.set.type.update= Distribution Set Type is already assigned to targets and cannot be changed
|
||||
message.target.ds.assign.success = Assignments saved successfully !
|
||||
message.no.directory.upload = Directory upload is not supported
|
||||
|
||||
message.delete.filter.confirm = Are you sure that you want to delete custom filter?
|
||||
message.delete.filter.success = Custom filter {0} deleted Successfully!
|
||||
message.create.filter.success = Custom filter {0} created Successfully!
|
||||
message.update.filter.success = Custom filter updated Successfully!
|
||||
message.target.filter.validation = Please enter name and query
|
||||
message.target.filter.duplicate = {0} already exists, please enter another value
|
||||
message.tag.use.bulk.upload = {0} cannot be deleted .It is in use in targets bulk upload
|
||||
message.bulk.upload.tag.assignment.failed = Tag {0} assignment failed as tag no longer exists
|
||||
message.bulk.upload.tag.assignments.failed= Few tag assignments failed as tags no longer exists
|
||||
|
||||
#reused messages
|
||||
soft.module.jvm =Runtime
|
||||
soft.module.application =Application
|
||||
soft.module.os =OS
|
||||
|
||||
#Artifact upload
|
||||
message.error.noFileSelected = No file selected for upload
|
||||
message.error.noProvidedName = Please provide custom file name
|
||||
message.error.noSwModuleSelected = Please select a Software Module
|
||||
message.no.duplicateFiles = Duplicate files selected
|
||||
message.no.duplicateFile = Duplicate file selected :
|
||||
message.delete.artifact = Are you sure that you want to delete artifact {0} ?
|
||||
message.duplicate.filename = Duplicate file name
|
||||
message.swModule.deleted = {0} Software Module(s) deleted
|
||||
message.error.missing.tagname = Please select tag name
|
||||
message.upload.failed = Streaming Failed
|
||||
message.uploadedfile.size.exceeded = File size exceeded .Allowed size {0} bytes
|
||||
message.uploadedfile.aborted = File upload aborted
|
||||
message.file.not.found = File not found
|
||||
message.artifact.deleted =Artifact with file {0} deleted successfully
|
||||
message.abort.upload = Are you sure that you want to abort the upload?
|
||||
|
||||
|
||||
|
||||
upload.swModuleTable.header = Software Module
|
||||
upload.selectedfile.name = file selected for upload
|
||||
upload.file.name = File name
|
||||
upload.sha1 = SHA1 checksum
|
||||
upload.md5 = MD5 checksum
|
||||
upload.last.modified.date=Last modified date
|
||||
upload.failed = Failed
|
||||
upload.success = Success
|
||||
upload.caption.add.new.swmodule = Configure New Software Module
|
||||
upload.caption.delete.swmodule = Configure Delete Software Module
|
||||
upload.swmodule.type = Type
|
||||
upload.artifact.alreadyExists = Artifact will be overridden as the given name already exists
|
||||
upload.size = Size(B)
|
||||
upload.validation = Validation
|
||||
upload.reason = Reason
|
||||
upload.action = Action
|
||||
upload.result.status = Upload status
|
||||
upload.file = Upload File
|
||||
upload.caption.update.swmodule = Update Software Module
|
||||
caption.tab.details = Details
|
||||
caption.tab.description = Description
|
||||
|
||||
caption.delete.artifact.confirmbox = Confirm Artifact Delete Action
|
||||
|
||||
#Manage distributions view
|
||||
label.drop.dist.delete.area = Drop here<br>to delete
|
||||
caption.assign.software.dist.accordion.tab = Assign Software Modules
|
||||
message.software.assignment = {0} Assignment(s) done
|
||||
message.dist.inuse = {0} Distribution is already assigned to target
|
||||
message.software.dist.already.assigned = {0} Distribution already has Software Module {1}
|
||||
message.software.dist.type.notallowed = {0} Software Module type can not assign to Distribution {1}
|
||||
message.target.assigned = {0} is assigned to {1}
|
||||
message.dist.type.delete = {0} DistributionType(s) Deleted successfully.
|
||||
message.sw.module.type.delete = {0} Software Module Type(s) deleted successfully.
|
||||
message.dist.type.discard.success = All Distribution Types are discarded successfully !
|
||||
message.dist.discard.success = All Distributions are discarded successfully !
|
||||
message.assign.discard.success = All assignments are discarded successfully !
|
||||
message.bulk.upload.assignment.failed = Distribution set assignment failed as distribution set no longer exists!
|
||||
message.key.missing = Key is missing !
|
||||
message.value.missing = Value is missing !
|
||||
message.metadata.saved = Metadata with key {0} successfully saved !
|
||||
message.metadata.updated = Metadata with key {0} successfully updated !
|
||||
message.metadata.duplicate.check = Metadata with key {0} already exists, please enter another value
|
||||
message.metadata.deleted.successfully = Metadata with key {0} successfully deleted !
|
||||
message.confirm.delete.metadata = Are you sure that you want to delete metadata with key {0} ?
|
||||
message.error.notification.ds.target.assigned = Distribution set {0}:{1} is already assigned to targets and cannot be changed
|
||||
|
||||
|
||||
# Login view
|
||||
notification.login.title=Welcome to Bosch IoT Software Provisioning.
|
||||
notification.login.description=Please login with your Bosch Identity Management credentials.
|
||||
notification.login.failed.title=Login failed!
|
||||
notification.login.failed.description=Login with the given credentials failed.
|
||||
notification.login.failed.credentialsexpired.title=Passwort Abgelaufen!
|
||||
notification.login.failed.credentialsexpired.description=Passwort ist entweder abgelaufen or muss initial gesetzt werden, bitte im Benutzer Verwaltungssystem <20>ndern.
|
||||
label.login.tenant=Tenant
|
||||
label.login.username=Username
|
||||
label.login.password=Password
|
||||
button.login.signin=Sign in
|
||||
checkbox.login.rememberme=Remember me
|
||||
|
||||
# Links
|
||||
link.documentation.name=Dokumentation
|
||||
link.demo.name=Demo
|
||||
link.requestaccount.name=Beantragung eines Accounts
|
||||
link.support.name=Support
|
||||
link.usermanagement.name=Benutzerverwaltung
|
||||
|
||||
|
||||
# System Configuration View
|
||||
notification.configuration.save=Ver<EFBFBD>nderungen gespeichert
|
||||
configuration.defaultdistributionset.title=Distribution Set Typ
|
||||
configuration.defaultdistributionset.select.label=Wahl des default Distribution Set typs:
|
||||
configuration.savebutton.tooltip=Konfigurationen speichern
|
||||
configuration.cancellbutton.tooltip=Konfigurationen zur<75>cksetzen
|
||||
configuration.authentication.title=Authentifikationseinstellungen
|
||||
|
||||
#Calendar
|
||||
calendar.year=Jahr
|
||||
calendar.years=Jahre
|
||||
calendar.month=Monat
|
||||
calendar.months=Monate
|
||||
calendar.day=Tag
|
||||
calendar.days=Tage
|
||||
calendar.hour=Stunde
|
||||
calendar.hours=Stunden
|
||||
calendar.minute=Minute
|
||||
calendar.minutes=Minuten
|
||||
calendar.second=Sekunde
|
||||
calendar.seconds=Sekunden
|
||||
|
||||
header.name = Name
|
||||
header.vendor = Vendor
|
||||
header.version = Version
|
||||
header.description = Description
|
||||
header.createdBy = Created By
|
||||
header.createdDate = Created Date
|
||||
header.modifiedBy = Modified By
|
||||
header.modifiedDate = Modified Date
|
||||
header.delete = Delete
|
||||
header.assigned.ds = Assigned DS
|
||||
header.installed.ds = Installed DS
|
||||
header.status = Status
|
||||
header.target.tags = Tags
|
||||
header.distributionset = Distribution set
|
||||
header.numberofgroups = No. of groups
|
||||
header.detail.status = Detail status
|
||||
header.total.targets = Total targets
|
||||
header.type = Type
|
||||
header.swmodules = SwModules
|
||||
header.migrations.step=IsRequiredMigrationStep
|
||||
|
||||
header.action=Actions
|
||||
header.action.run=Run
|
||||
header.action.pause=Pause
|
||||
header.action.update=Edit
|
||||
|
||||
header.rolloutgroup.installed.percentage = % Finished
|
||||
header.rolloutgroup.threshold.error = Error threshold
|
||||
header.rolloutgroup.threshold = Trigger threshold
|
||||
|
||||
header.rolloutgroup.target.date = Date and time
|
||||
header.rolloutgroup.target.message = Messages
|
||||
|
||||
rollout.group.label.target.truncated = {0} targets has been truncated in the list due the target size limit of {1}
|
||||
|
||||
distribution.details.header = Distribution Set
|
||||
target.details.header = Target
|
||||
header.caption.mandatory = Mandatory
|
||||
header.caption.typename = SoftwareModuleType
|
||||
header.caption.softwaremodule = SoftwareModule
|
||||
header.caption.unassign = Unassign
|
||||
message.sw.unassigned = Software Module {0} successfully unassigned
|
||||
header.caption.upload.details = Upload details
|
||||
header.key = Key
|
||||
header.value = Value
|
||||
combo.type.tag.name = Type tag name
|
||||
|
||||
label.yes = Yes
|
||||
label.no =No
|
||||
|
||||
|
||||
#Menu
|
||||
menu.title = Software Provisioning
|
||||
|
||||
|
||||
#Rollout management
|
||||
prompt.number.of.groups = Number of groups
|
||||
prompt.tigger.threshold = Trigger threshold
|
||||
prompt.error.threshold = Error threshold
|
||||
prompt.distribution.set = Distribution Set
|
||||
caption.configure.rollout = Configure Rollout
|
||||
caption.update.rollout = Update Rollout
|
||||
prompt.target.filter = Custom Target Filter
|
||||
message.rollout.nonzero.group.number = Number of groups must be greater than zero
|
||||
message.rollout.max.group.number = Number of groups must not be greater than 500
|
||||
message.rollout.duplicate.check = Rollout [ {0} ] must be unique, entered value already exists.
|
||||
message.rollout.field.value.range = Value should be in range {0} to {1}
|
||||
message.correct.invalid.value = Please correct invalid values
|
||||
message.enter.number = Please enter number
|
||||
message.rollout.started = Rollout {0} started successfully
|
||||
message.rollout.paused = Rollout {0} paused successfully
|
||||
message.rollout.resumed = Rollout {0} resumed successfully
|
||||
message.rollout.noofgroups.or.targetfilter.missing = Please enter number of groups and select target filter
|
||||
message.rollouts = Rollouts
|
||||
label.target.per.group = Targets per group :
|
||||
message.dist.already.assigned = Distribution {0} is already assigned to target
|
||||
message.error.creating.rollout = Server error. Error creating Rollout. Please contact the administrator
|
||||
message.error.starting.rollout = Server error. Error starting Rollout. Please contact the administrator
|
||||
|
||||
#Target Filter Management
|
||||
breadcrumb.target.filter.custom.filters = Custom Filters
|
||||
@@ -1,502 +0,0 @@
|
||||
#
|
||||
# Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
#
|
||||
# All rights reserved. This program and the accompanying materials
|
||||
# are made available under the terms of the Eclipse Public License v1.0
|
||||
# which accompanies this distribution, and is available at
|
||||
# http://www.eclipse.org/legal/epl-v10.html
|
||||
#
|
||||
|
||||
#########################################################################################
|
||||
# This is the messages_en.properties file
|
||||
#########################################################################################
|
||||
|
||||
# Button names prefix with - button
|
||||
button.save = Save
|
||||
button.delete = Delete
|
||||
button.discard = Discard
|
||||
button.discard.all = Discard All
|
||||
button.delete.all = Delete All
|
||||
button.assign.all = Save Assign
|
||||
button.actions = You have actions
|
||||
button.no.actions = No actions
|
||||
button.ok = OK
|
||||
button.cancel = Cancel
|
||||
button.upload.file = Upload File
|
||||
bulk.targets.upload = Please upload csv file.
|
||||
bulkupload.ds.name = DS Name
|
||||
|
||||
# Headers prefix with - header
|
||||
header.target.table=Targets
|
||||
header.dist.table=Distributions
|
||||
header.filter.tag=Filter by Tag
|
||||
header.target.filter.tag=Filters
|
||||
header.first.assignment.table = Targets
|
||||
header.second.assignment.table = Distributions
|
||||
header.dist.first.assignment.table = Distributions
|
||||
header.dist.second.assignment.table = Software Modules
|
||||
header.third.assignment.table = Discard
|
||||
header.one.deletedist.table = Distribution Name
|
||||
header.second.deletedist.table = Discard Changes
|
||||
header.first.deletetarget.table = Target Name
|
||||
header.second.deletetarget.table = Discard Changes
|
||||
header.first.deleteswmodule.table = Delete software
|
||||
header.first.delete.dist.type.table = DistributionSetType
|
||||
header.second.delete.dist.type.table = Discard
|
||||
header.first.delete.swmodule.type.table = Software Module Type
|
||||
header.second.delete.swmodule.type.table = Discard
|
||||
header.dist.twintable.selected=Selected
|
||||
header.dist.twintable.available=Available
|
||||
header.target.installed = Installed
|
||||
header.target.assigned = Assigned
|
||||
|
||||
# Captions prefix with - caption
|
||||
caption.action.history = Action history for {0}
|
||||
caption.error = Error
|
||||
caption.new.softwaremodule.application = Configure New Application
|
||||
caption.new.softwaremodule.jvm = Configure New Runtime
|
||||
caption.new.softwaremodule.os = Configure New OS
|
||||
caption.filter.simple = Simple Filter
|
||||
caption.filter.custom = Custom Filter
|
||||
caption.metadata = Metadata
|
||||
|
||||
caption.add.softwaremodule = Configure Software Module
|
||||
caption.add.new.dist = Configure New Distribution
|
||||
caption.update.dist = Configure Update Distribution
|
||||
caption.add.tag = Configure Tag
|
||||
caption.add.type = Configure Type
|
||||
caption.add.new.target = Configure New Target
|
||||
caption.update.target = Configure Update Target
|
||||
caption.bulk.upload.targets = Bulk Upload
|
||||
caption.softwares.distdetail.tab = Modules
|
||||
caption.tags.tab = Tags
|
||||
caption.logs.tab = Logs
|
||||
caption.attributes.tab = Attributes
|
||||
caption.types.tab = Types
|
||||
caption.save.window = Action Details
|
||||
caption.assign.dist.accordion.tab = Assign Software Module
|
||||
caption.delete.dist.accordion.tab = Delete Distributions
|
||||
caption.delete.target.accordion.tab = Delete Targets
|
||||
caption.delete.swmodule.accordion.tab = Delete SW Modules
|
||||
caption.delete.dist.set.type.accordion.tab = Delete Distribution Set Type
|
||||
caption.delete.sw.module.type.accordion.tab = Delete Software Module Type
|
||||
caption.attributes = Attributes
|
||||
caption.panel.dist.installed = Installed Distribution Set
|
||||
caption.panel.dist.assigned = Assigned Distribution Set
|
||||
caption.soft.delete.confirmbox = Confirm Software Module Delete Action
|
||||
caption.cancel.action.confirmbox = Confirm Action Cancellation
|
||||
caption.forced.datefield = Force update at time
|
||||
caption.force.action.confirmbox = Confirm Force Active Action
|
||||
caption.filter.delete.confirmbox = Confirm Filter Delete Action
|
||||
caption.metadata.popup = Metadata of
|
||||
caption.metadata.delete.action.confirmbox = Confirm Metadata Delete Action
|
||||
|
||||
caption.confirm.abort.action = Confirm Abort Action
|
||||
|
||||
# Labels prefix with - label
|
||||
label.dist.details.type = Type :
|
||||
label.dist.details.name = Name :
|
||||
label.dist.details.version = Version :
|
||||
label.dist.details.vendor = Vendor :
|
||||
label.dist.details.jvm = Runtime :
|
||||
label.dist.details.ah = Application :
|
||||
label.dist.details.os = OS :
|
||||
label.modified.date = Last modified at :
|
||||
label.modified.by = Last modified by :
|
||||
label.created.at = Created at :
|
||||
label.created.by = Created by :
|
||||
label.target.count = Targets :
|
||||
label.description = Description :
|
||||
label.ip = Address :
|
||||
label.type = Type :
|
||||
label.assigned.type = Assignment type :
|
||||
label.assigned.count = {0} Assigned
|
||||
label.installed.count = {0} Installed
|
||||
label.mandatory.field = * Mandatory Field
|
||||
label.components.drop.area = Drop here to delete
|
||||
label.software.module.drop.area = Delete Software
|
||||
label.create.tag = Create Tag
|
||||
label.update.tag = Update Tag
|
||||
label.create.type = Create Type
|
||||
label.update.type = Update Type
|
||||
label.singleAssign.type = Firmware (FW)
|
||||
label.multiAssign.type = Software (SW)
|
||||
label.choose.type = Choose Type to Update
|
||||
label.choose.type.color = Choose Type Color
|
||||
label.combobox.type = Select Type
|
||||
label.combobox.tag = Select Tag
|
||||
label.choose.tag = Choose Tag to update
|
||||
label.choose.tag.color = Choose Tag Color
|
||||
label.target.filtered.total = Total filtered targets :
|
||||
label.filter.selected = Selected:
|
||||
label.filter.shown = Shown:
|
||||
label.filter.targets = Filtered targets :
|
||||
label.filter = Filter :
|
||||
label.target.filter.count = Total Targets :
|
||||
label.filter.selected = Selected :
|
||||
label.filter.shown = Shown :
|
||||
label.filter.status = Status,
|
||||
label.filter.overdue = Overdue,
|
||||
label.filter.tags = Tags,
|
||||
label.filter.text = Search Text
|
||||
label.filter.dist = Distribution,
|
||||
label.filter.custom = Custom
|
||||
label.target.filter.truncated={0} targets has been truncated in the list due the target size limit of {1}, use filters to reduce the targets to be shown
|
||||
label.active =Active
|
||||
label.inactive = In-active
|
||||
label.finished = Finished
|
||||
label.error = Error
|
||||
label.warning = Warning
|
||||
label.running = Running
|
||||
label.cancelled = Cancelled
|
||||
label.cancelling = Canceling
|
||||
label.retrieved = Retrieved
|
||||
label.download = Downloading
|
||||
label.scheduled = Scheduled
|
||||
label.target.id = Controller Id :
|
||||
label.target.ip = Controller IP :
|
||||
label.target.security.token = Security token :
|
||||
label.filter.by.status = Filter by Status
|
||||
label.filter.by.overdue = Filter by Overdue
|
||||
label.target.controller.attrs = <b>Controller attributes</b>
|
||||
label.target.lastpolldate = Last poll :
|
||||
label.no.tag.assigned = NO TAG
|
||||
label.tag.name = Tag name
|
||||
label.configuration.auth.header = Allow targets to authenticate via a certificate authenticated by an reverse proxy
|
||||
label.configuration.auth.gatewaytoken = Allow a gateway to authenticate and manage multiple targets through a gateway security token
|
||||
label.configuration.auth.targettoken = Allow targets to authenticate directly with their target security token
|
||||
label.configuration.anonymous.download = Allow targets to download artifacts without security credentials
|
||||
label.unsupported.browser.ie=Sorry! current browser is not supported. Please use Internet Explorer 11 and above
|
||||
|
||||
# Checkbox label prefix with - checkbox
|
||||
checkbox.dist.migration.required = Required Migration Step :
|
||||
checkbox.dist.required.migration.step = Required Migration Step
|
||||
|
||||
# TextFields prefix with - textfield
|
||||
textfield.name = Name
|
||||
textfield.key = Key
|
||||
textfield.version = Version
|
||||
textfield.vendor = Vendor
|
||||
textfield.description = Description
|
||||
textfield.customfiltername = Filter name
|
||||
textfield.value = Value
|
||||
ui.version = Powered by Bosch IoT Software Provisioning
|
||||
prompt.target.id = Controller ID
|
||||
|
||||
|
||||
#Tooltips prefix with - tooltip
|
||||
tooltip.add.module = Add Software Module
|
||||
tooltip.status.unknown = Unknown
|
||||
tooltip.status.registered = Registered
|
||||
tooltip.status.pending = Pending
|
||||
tooltip.status.error = Error
|
||||
tooltip.status.insync = In-sync
|
||||
tooltip.status.overdue = Overdue
|
||||
tooltip.delete.module = Select and delete Software Module
|
||||
tooltip.forced.item=Forced update action
|
||||
tooltip.soft.item=Soft update action which interacts with an user as a attempt update action for example
|
||||
tooltip.timeforced.item=Soft update until a specific time and then the action will be forced
|
||||
tooltip.check.for.mandatory=Check to make Mandatory
|
||||
tooltip.artifact.icon=Show Artifact Details
|
||||
tooltip.click.to.edit = Click to edit
|
||||
tooltip.metadata.icon = Manage Metadata
|
||||
|
||||
|
||||
# Notification messages prefix with - message
|
||||
message.save.success = {0} saved successfully
|
||||
message.update.success = {0} updated successfully
|
||||
message.delete.success = {0} deleted successfully
|
||||
message.dist.installedorassigned = Target {targId} is already assigned/installed with distribution
|
||||
message.dist.pending.action = Target {0} is already assigned with distribution {1} . Pending for action
|
||||
message.empty.target.tags= No Tags Created
|
||||
message.empty.disttype.tags = No Distribution type tags created
|
||||
message.select.row = Please select a row to drag
|
||||
message.error = Unknown error occured during the operation. Please contact administrator
|
||||
message.dist.assigned.one = {0} is assigned to {1}
|
||||
message.dist.assigned.many = {0} DistributionSets are assigned to {1}
|
||||
message.dist.unassigned.one = {0} is unassigned from {1}
|
||||
message.dist.unassigned.many = {0} DistributionSets are unassigned from {1}
|
||||
message.target.assigned.one = {0} is assigned to {1}
|
||||
message.target.assigned.many = {0} Targets are assigned to {1}
|
||||
message.target.unassigned.one = {0} is unassigned from {1}
|
||||
message.target.unassigned.many = {0} Targets are unassigned from {1}
|
||||
message.target.assigned.pending = Some target(s) are already assigned.Pending for action
|
||||
message.cannot.delete = Cannot be deleted
|
||||
message.check.softwaremodule = Please provide both name and version!
|
||||
message.duplicate.softwaremodule = {0} : {1} already exists!
|
||||
message.tag.delete = Please unclick the tag {0}, then try to delete
|
||||
message.dist.type.check.delete = Please unclick the distribution type {0}, then try to delete
|
||||
message.swmodule.type.check.delete = Please unclick the Software Module type {0}, then try to delete
|
||||
message.targets.already.deleted = Few Target(s) are already deleted.Pending for action
|
||||
message.dists.already.deleted = Few distribution(s) are already deleted.Pending for action
|
||||
message.target.deleted.pending = Target(s) already deleted.Pending for action
|
||||
message.dist.deleted.pending = Distribution(s) already deleted.Pending for action
|
||||
message.dist.delete.success = All selected distribution sets are deleted successfully !
|
||||
message.dist.discard.success = All distributions selected for delete are discarded successfully !
|
||||
message.target.delete.success = All selected targets are deleted successfully !
|
||||
message.target.discard.success = All targets selected for delete are discarded successfully !
|
||||
message.software.discard.success = All software modules selected for delete are discarded successfully !
|
||||
message.software.type.discard.success = All software moduleTypes selected for delete are discarded successfully !
|
||||
message.assign.software.discard.success = All software moduleTypes selected for assign are discarded successfully !
|
||||
message.software.delete.success = All software modules selected for delete are deleted successfully !
|
||||
message.software.type.delete.success = All software modules types selected for delete are deleted successfully !
|
||||
message.dist.set.type.deleted.success = {0} DistributionSetType deleted successfully !
|
||||
message.new.dist.save.success = {0} - {1} saved successfully
|
||||
message.dist.update.success = {0} - {1} updated successfully
|
||||
message.duplicate.dist = Distribution set [{0}] or version [{1}] must be unique, entered value already exists.
|
||||
message.error.view = No such view: {0}
|
||||
message.accessdenied.view = No access to view: {0}
|
||||
message.no.data = No data available
|
||||
message.target.assignment = {0} Assignment(s) done
|
||||
message.target.deleted = {0} Target(s) deleted
|
||||
message.dist.deleted = {0} Distribution set(s) deleted
|
||||
message.tag.update.mandatory = Please select the Tag to update
|
||||
message.tag.duplicate.check = {0} already exists, please enter another value
|
||||
message.type.key.duplicate.check = Distribution type with key {0} already exists, please give another value
|
||||
message.type.key.swmodule.duplicate.check = Software Module type with key {0} already exists, please give another value
|
||||
message.no.action.history = No action history is available for the target : {0}
|
||||
message.no.available = --No messages available--
|
||||
message.no.actionupdateds.available = No other updates available for this action
|
||||
message.mandatory.check = Mandatory details are missing
|
||||
message.target.duplicate.check = Target [ {0} ] must be unique, entered value already exists.
|
||||
message.permission.insufficient = Insufficient permissions to perform this action.
|
||||
message.error.temp = The operation cannot be fulfilled due to {0}. Please contact administrator
|
||||
message.dist.alreadyassigned = {0} : {1} is already assigned/installed, cannot be updated
|
||||
message.dist.tag.alreadyassigned = {0} : {1} is already assigned/installed, cannot assign/un-assign to tag
|
||||
message.dists.unassign.tag.alreadyassigned = Few of the DistributionSet's are already assigned to Target, those cannot be unassigned from Tag"
|
||||
message.dists.assign.tag.alreadyassigned = Few of the DistributionSet's are already assigned to Target, those cannot be assigned to Tag"
|
||||
message.dists.tag.assigned = {0} DistributionSet's assigned to Tag {1}
|
||||
message.dists.tag.unassigned = {0} DistributionSet's un-assigned from Tag {1}
|
||||
message.dist.no.operation = {0} - already assigned/installed, No operation
|
||||
message.sm.delete.confirm = Are you sure that you want to delete the selected {0} Software Module?
|
||||
message.error.os.softmodule = Please select the OS to delete
|
||||
message.error.ah.softmodule = Please select the Application to delete
|
||||
message.error.softmodule.deleted = The selected Software Module is already deleted
|
||||
message.cancel.action = Cancel
|
||||
message.cancel.action.success = Action cancelled successfully !
|
||||
message.cancel.action.failed = Unable to cancel the action !
|
||||
message.cancel.action.confirm = Are you sure that you want to cancel this action?
|
||||
message.target.alreadyAssigned = {0} Target(s) were already assigned
|
||||
message.dist.alreadyAssigned = {0} Distribution Set(s) were already assigned
|
||||
message.force.action = Force
|
||||
message.force.action.confirm = Are you sure that you want to force this action?
|
||||
message.force.action.success = Action forced successfully !
|
||||
message.distribution.no.update = distribution {0} set is already assigned to targets and cannot be changed
|
||||
message.action.not.allowed = Action not allowed
|
||||
message.action.did.not.work = Action did not work. Please try again.
|
||||
message.onlyone.distribution.assigned = Only one distribution set can be assigned
|
||||
message.onlyone.distribution.dropallowed = Only one distribution set can be dropped
|
||||
message.error.missing.typename = Missing Type Name
|
||||
message.error.missing.typenameorkey = Missing Type Name or Key or Software Module type
|
||||
message.tag.cannot.be.assigned = Target/DS cannot be assigned to {0}
|
||||
message.no.targets.assiged.fortag = No targets are assigned to tag {0}
|
||||
message.error.missing.tagname = Please select tag name
|
||||
message.type.delete = Please unclick the distribution type {0}, then try to delete
|
||||
message.error.dist.set.type.update= Distribution Set Type is already assigned to targets and cannot be changed
|
||||
message.target.ds.assign.success = Assignments saved successfully !
|
||||
message.no.directory.upload = Directory upload is not supported
|
||||
|
||||
message.delete.filter.confirm = Are you sure that you want to delete custom filter?
|
||||
message.delete.filter.success = Custom filter {0} deleted Successfully!
|
||||
message.create.filter.success = Custom filter {0} created Successfully!
|
||||
message.update.filter.success = Custom filter updated Successfully!
|
||||
message.target.filter.validation = Please enter name and query
|
||||
message.target.filter.duplicate = {0} already exists, please enter another value
|
||||
|
||||
# action info
|
||||
action.target.table.selectall = Select all (Ctrl+A)
|
||||
action.target.table.clear = Clear selections
|
||||
|
||||
#reused messages
|
||||
soft.module.jvm =Runtime
|
||||
soft.module.application =Application
|
||||
soft.module.os =OS
|
||||
|
||||
#Artifact upload
|
||||
message.error.noFileSelected = No file selected for upload
|
||||
message.error.noProvidedName = Please provide custom file name
|
||||
message.error.noSwModuleSelected = Please select a Software Module
|
||||
message.no.duplicateFiles = Duplicate files selected
|
||||
message.no.duplicateFile = Duplicate file selected :
|
||||
message.delete.artifact = Are you sure that you want to delete artifact {0} ?
|
||||
message.duplicate.filename = Duplicate file name
|
||||
message.swModule.deleted = {0} Software Module(s) deleted
|
||||
message.upload.failed = Streaming Failed
|
||||
message.uploadedfile.size.exceeded = File size exceeded .Allowed size {0} bytes
|
||||
message.uploadedfile.aborted = File upload aborted
|
||||
message.file.not.found = File not found
|
||||
message.abort.upload = Are you sure that you want to abort the upload?
|
||||
|
||||
|
||||
upload.swModuleTable.header = Software Module
|
||||
upload.selectedfile.name = file selected for upload
|
||||
upload.file.name = File name
|
||||
upload.sha1 = SHA1 checksum
|
||||
upload.md5 = MD5 checksum
|
||||
upload.last.modified.date=Last modified date
|
||||
upload.failed = Failed
|
||||
upload.success = Success
|
||||
upload.caption.add.new.swmodule = Configure New Software Module
|
||||
upload.caption.delete.swmodule = Configure Delete Software Module
|
||||
upload.swmodule.type = Type
|
||||
upload.artifact.alreadyExists = Artifact will be overridden as the given name already exists
|
||||
upload.size = Size(B)
|
||||
upload.validation = Validation
|
||||
upload.reason = Reason
|
||||
upload.action = Action
|
||||
upload.result.status = Upload status
|
||||
upload.file = Upload File
|
||||
upload.caption.update.swmodule = Update Software Module
|
||||
caption.tab.details = Details
|
||||
caption.tab.description = Description
|
||||
|
||||
caption.delete.artifact.confirmbox = Confirm Artifact Delete Action
|
||||
|
||||
|
||||
#Manage distributions view
|
||||
label.drop.dist.delete.area = Drop here<br>to delete
|
||||
caption.assign.software.dist.accordion.tab = Assign Software Modules
|
||||
message.software.assignment = {0} Software Module(s) Assignment(s) done
|
||||
message.dist.inuse = {0} Distribution is already assigned to target
|
||||
message.software.dist.already.assigned = {0} Distribution already has Software Module {1}
|
||||
message.software.dist.type.notallowed = {0} Software Module type can not assign to Distribution {1}
|
||||
message.target.assigned = {0} is assigned to {1}
|
||||
message.dist.type.delete = {0} DistributionType(s) Deleted successfully.
|
||||
message.sw.module.type.delete = {0} Software Module Type(s) deleted successfully.
|
||||
message.dist.type.discard.success = All Distribution Types are discarded successfully !
|
||||
message.dist.discard.success = All Distributions are discarded successfully !
|
||||
message.assign.discard.success = All assignments are discarded successfully !
|
||||
message.key.missing = Key is missing !
|
||||
message.value.missing = Value is missing !
|
||||
message.metadata.saved = Metadata with key {0} successfully saved !
|
||||
message.metadata.updated = Metadata with key {0} successfully updated !
|
||||
message.metadata.duplicate.check = Metadata with key {0} already exists, please enter another value
|
||||
message.metadata.deleted.successfully = Metadata with key {0} successfully deleted !
|
||||
message.confirm.delete.metadata = Are you sure that you want to delete metadata with key {0} ?
|
||||
message.error.notification.ds.target.assigned = Distribution set {0}:{1} is already assigned to targets and cannot be changed
|
||||
|
||||
# Login view
|
||||
notification.login.title=Welcome to Bosch IoT Software Provisioning.
|
||||
notification.login.description=Please login with your Bosch Identity Management credentials.
|
||||
notification.login.failed.title=Login failed!
|
||||
notification.login.failed.description=Login with the given credentials failed.
|
||||
notification.login.failed.credentialsexpired.title=Password Expired!
|
||||
notification.login.failed.credentialsexpired.description=Password has been expired or needs to be set initially, please visit the User Management to change or set your password.
|
||||
label.login.tenant=Tenant
|
||||
label.login.username=Username
|
||||
label.login.password=Password
|
||||
button.login.signin=Sign in
|
||||
checkbox.login.rememberme=Remember me
|
||||
|
||||
# Links
|
||||
link.documentation.name=Documentation
|
||||
link.demo.name=Demo
|
||||
link.requestaccount.name=Request Account
|
||||
link.support.name=Support
|
||||
link.usermanagement.name=User Management
|
||||
|
||||
# System Configuration View
|
||||
notification.configuration.save=Saved changes
|
||||
configuration.defaultdistributionset.title=Distribution Set Type
|
||||
configuration.defaultdistributionset.select.label=Select the default Distribution Set type:
|
||||
configuration.savebutton.tooltip=Save Configurations
|
||||
configuration.cancellbutton.tooltip=Cancel Configurations
|
||||
configuration.authentication.title=Authentication Configuration
|
||||
controller.polling.title=Polling Configuration
|
||||
controller.polling.time=Polling Time
|
||||
controller.polling.overduetime=Polling Overdue Time
|
||||
|
||||
#Calendar
|
||||
calendar.year=year
|
||||
calendar.years=years
|
||||
calendar.month=month
|
||||
calendar.months=months
|
||||
calendar.day=day
|
||||
calendar.days=days
|
||||
calendar.hour=hour
|
||||
calendar.hours=hours
|
||||
calendar.minute=minute
|
||||
calendar.minutes=minutes
|
||||
calendar.second=second
|
||||
calendar.seconds=seconds
|
||||
|
||||
header.name = Name
|
||||
header.vendor = Vendor
|
||||
header.version = Version
|
||||
header.description = Description
|
||||
header.createdBy = Created By
|
||||
header.createdDate = Created Date
|
||||
header.modifiedBy = Modified By
|
||||
header.modifiedDate = Modified Date
|
||||
header.delete = Delete
|
||||
header.assigned.ds = Assigned DS
|
||||
header.installed.ds = Installed DS
|
||||
header.status = Status
|
||||
header.target.tags = Tags
|
||||
header.distributionset = Distribution Set
|
||||
header.numberofgroups = No. of groups
|
||||
header.detail.status = Detail status
|
||||
header.total.targets = Total targets
|
||||
header.type = Type
|
||||
header.swmodules = SwModules
|
||||
header.migrations.step=IsRequiredMigrationStep
|
||||
|
||||
header.action=Actions
|
||||
header.action.run=Run
|
||||
header.action.pause=Pause
|
||||
header.action.update=Edit
|
||||
|
||||
header.rolloutgroup.installed.percentage = % Finished
|
||||
header.rolloutgroup.threshold.error = Error threshold
|
||||
header.rolloutgroup.threshold = Trigger threshold
|
||||
|
||||
|
||||
|
||||
header.rolloutgroup.target.date = Date and time
|
||||
header.rolloutgroup.target.message = Messages
|
||||
|
||||
rollout.group.label.target.truncated = {0} targets has been truncated in the list due the target size limit of {1}
|
||||
|
||||
distribution.details.header = Distribution Set
|
||||
target.details.header = Target
|
||||
header.caption.mandatory = Mandatory
|
||||
header.caption.typename = SoftwareModuleType
|
||||
header.caption.softwaremodule = SoftwareModule
|
||||
header.caption.upload.details = Upload details
|
||||
header.key = Key
|
||||
header.value = Value
|
||||
combo.type.tag.name = Type tag name
|
||||
|
||||
label.yes = Yes
|
||||
label.no =No
|
||||
|
||||
#Menu
|
||||
menu.title = Software Provisioning
|
||||
|
||||
|
||||
#Rollout management
|
||||
prompt.number.of.groups = Number of groups
|
||||
prompt.tigger.threshold = Trigger threshold
|
||||
prompt.error.threshold = Error threshold
|
||||
prompt.distribution.set = Distribution Set
|
||||
caption.configure.rollout = Configure Rollout
|
||||
caption.update.rollout = Update Rollout
|
||||
prompt.target.filter = Custom Target Filter
|
||||
message.rollout.nonzero.group.number = Number of groups must be greater than zero
|
||||
message.rollout.max.group.number = Number of groups must not be greater than 500
|
||||
message.rollout.duplicate.check = Rollout [ {0} ] must be unique, entered value already exists.
|
||||
message.correct.invalid.value = Please correct invalid values
|
||||
message.enter.number = Please enter number
|
||||
message.rollout.field.value.range = Value should be in range {0} to {1}
|
||||
message.rollout.started = Rollout {0} started successfully
|
||||
message.rollout.paused = Rollout {0} paused successfully
|
||||
message.rollout.resumed = Rollout {0} resumed successfully
|
||||
message.rollout.noofgroups.or.targetfilter.missing = Please enter number of groups and select target filter
|
||||
message.rollouts = Rollouts
|
||||
label.target.per.group = Targets per group :
|
||||
message.dist.already.assigned = Distribution {0} is already assigned to target
|
||||
message.error.creating.rollout = Server error. Error creating Rollout. Please contact the administrator
|
||||
message.error.starting.rollout = Server error. Error starting Rollout. Please contact the administrator
|
||||
|
||||
#Target Filter Management
|
||||
breadcrumb.target.filter.custom.filters = Custom Filters
|
||||
2
pom.xml
2
pom.xml
@@ -149,7 +149,7 @@
|
||||
<!-- Misc libraries versions - END -->
|
||||
|
||||
<!-- 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 -->
|
||||
|
||||
Reference in New Issue
Block a user