Added auto assign distribution set to target filter query feature

Signed-off-by: Dominik Herbst <dominik.herbst@bosch-si.com>
This commit is contained in:
Dominik Herbst
2016-09-01 10:19:55 +02:00
parent 93d509fbcd
commit 03e2ee81b8
54 changed files with 2922 additions and 1115 deletions

4
.gitignore vendored
View File

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

View File

@@ -0,0 +1,70 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* Describing the fields of the Target model which can be used in the REST API
* e.g. for sorting etc.
*
*/
public enum TargetFilterQueryFields implements FieldNameProvider {
/**
* The id field.
*/
ID("id"),
/**
* The name field.
*/
NAME("name"),
/**
* distribution set which is set as auto assign distribution set
*/
AUTOASSIGNDISTRIBUTIONSET("autoAssignDistributionSet", "name", "version");
private final String fieldName;
private List<String> subEntityAttributes;
private boolean mapField;
TargetFilterQueryFields(final String fieldName) {
this(fieldName, false, Collections.emptyList());
}
TargetFilterQueryFields(final String fieldName, final String... subEntityAttribues) {
this(fieldName, false, Arrays.asList(subEntityAttribues));
}
TargetFilterQueryFields(final String fieldName, final boolean mapField, final List<String> subEntityAttribues) {
this.fieldName = fieldName;
this.mapField = mapField;
this.subEntityAttributes = subEntityAttribues;
}
@Override
public List<String> getSubEntityAttributes() {
return subEntityAttributes;
}
@Override
public boolean isMap() {
return mapField;
}
@Override
public String getFieldName() {
return fieldName;
}
}

View File

@@ -0,0 +1,89 @@
/**
* Copyright (c) 2011-2015 Bosch Software Innovations GmbH, Germany. All rights reserved.
*/
package org.eclipse.hawkbit.mgmt.json.model.targetfilter;
import org.eclipse.hawkbit.mgmt.json.model.MgmtBaseEntity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* A json annotated rest model for Target to RESTful API representation.
*
*/
@JsonInclude(Include.ALWAYS)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtTargetFilterQuery extends MgmtBaseEntity {
@JsonProperty(value = "id", required = true)
private Long filterId;
@JsonProperty
private String name;
@JsonProperty
private String query;
@JsonProperty
private Long autoAssignDistributionSet;
/**
* @return the filterId
*/
public Long getFilterId() {
return filterId;
}
/**
* @param filterId
* the filterId to set
*/
public void setFilterId(final Long filterId) {
this.filterId = filterId;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name
* the name to set
*/
public void setName(final String name) {
this.name = name;
}
/**
* @return the query
*/
public String getQuery() {
return query;
}
/**
* @param query
* the query to set
*/
@JsonIgnore
public void setQuery(final String query) {
this.query = query;
}
public Long getAutoAssignDistributionSet() {
return autoAssignDistributionSet;
}
@JsonIgnore
public void setAutoAssignDistributionSet(final Long autoAssignDistributionSet) {
this.autoAssignDistributionSet = autoAssignDistributionSet;
}
}

View File

@@ -0,0 +1,50 @@
/**
* Copyright (c) 2011-2015 Bosch Software Innovations GmbH, Germany. All rights reserved.
*/
package org.eclipse.hawkbit.mgmt.json.model.targetfilter;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Request body for target PUT/POST commands.
*
*/
public class MgmtTargetFilterQueryRequestBody {
@JsonProperty(required = true)
private String name;
@JsonProperty(required = true)
private String query;
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name
* the name to set
*/
public MgmtTargetFilterQueryRequestBody setName(final String name) {
this.name = name;
return this;
}
/**
* @return the filter query
*/
public String getQuery() {
return query;
}
/**
* @param query
* the filter query
*/
public void setQuery(String query) {
this.query = query;
}
}

View File

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

View File

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

View File

@@ -0,0 +1,156 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.rest.api;
import org.eclipse.hawkbit.mgmt.json.model.MgmtId;
import org.eclipse.hawkbit.mgmt.json.model.PagedList;
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSet;
import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQuery;
import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQueryRequestBody;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
/**
* Api for handling target operations.
*/
@RequestMapping(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING)
public interface MgmtTargetFilterQueryRestApi {
/**
* Handles the GET request of retrieving a single target filter.
*
* @param filterId
* the ID of the target filter to retrieve
* @return a single target with status OK.
*/
@RequestMapping(method = RequestMethod.GET, value = "/{filterId}", produces = { "application/hal+json",
MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<MgmtTargetFilterQuery> getFilter(@PathVariable("filterId") final Long filterId);
/**
* Handles the GET request of retrieving all filters.
*
* @param pagingOffsetParam
* the offset of list of targets for pagination, might not be
* present in the rest request then default value will be applied
* @param pagingLimitParam
* the limit of the paged request, might not be present in the
* rest request then default value will be applied
* @param sortParam
* the sorting parameter in the request URL, syntax
* {@code field:direction, field:direction}
* @param rsqlParam
* the search parameter in the request URL, syntax
* {@code q=name==abc}
* @return a list of all targets for a defined or default page request with
* status OK. The response is always paged. In any failure the
* JsonResponseExceptionHandler is handling the response.
*/
@RequestMapping(method = RequestMethod.GET, produces = { "application/hal+json", MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<PagedList<MgmtTargetFilterQuery>> getFilters(
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) final int pagingOffsetParam,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) final int pagingLimitParam,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) final String sortParam,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) final String rsqlParam);
/**
* Handles the POST request of creating new target filters. The request body
* must always be a list of target filters.
*
* @param filter
* the filters to be created.
* @return In case all filters were successfully created the ResponseEntity
* with status code 201 with a list of successfully created entities
* is returned. In any failure the JsonResponseExceptionHandler is
* handling the response.
*/
@RequestMapping(method = RequestMethod.POST, consumes = { "application/hal+json",
MediaType.APPLICATION_JSON_VALUE }, produces = { "application/hal+json", MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<MgmtTargetFilterQuery> createFilter(@RequestBody final MgmtTargetFilterQueryRequestBody filter);
/**
* Handles the PUT request of updating a target filter. The ID is within the
* URL path of the request. A given ID in the request body is ignored. It's
* not possible to set fields to {@code null} values.
*
* @param filterId
* the path parameter which contains the ID of the target filter
* @param targetFilterRest
* the request body which contains the fields which should be
* updated, fields which are not given are ignored for the
* update.
* @return the updated target filter response which contains all fields
* including fields which have not been updated
*/
@RequestMapping(method = RequestMethod.PUT, value = "/{filterId}", consumes = { "application/hal+json",
MediaType.APPLICATION_JSON_VALUE }, produces = { "application/hal+json", MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<MgmtTargetFilterQuery> updateFilter(@PathVariable("filterId") final Long filterId,
@RequestBody final MgmtTargetFilterQueryRequestBody targetFilterRest);
/**
* Handles the DELETE request of deleting a target filter.
*
* @param filterId
* the ID of the target filter to be deleted
* @return If the given controllerId could exists and could be deleted Http
* OK. In any failure the JsonResponseExceptionHandler is handling
* the response.
*/
@RequestMapping(method = RequestMethod.DELETE, value = "/{filterId}", produces = { "application/hal+json",
MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<Void> deleteFilter(@PathVariable("filterId") final Long filterId);
/**
* Handles the GET request of retrieving the distribution set for auto
* assignment of an specific target filter.
*
* @param filterId
* the ID of the target to retrieve the assigned distribution
* @return the assigned distribution set with status OK, if none is assigned
* than {@code null} content (e.g. "{}")
*/
@RequestMapping(method = RequestMethod.GET, value = "/{filterId}/autoAssignDS", produces = { "application/hal+json",
MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<MgmtDistributionSet> getAssignedDistributionSet(@PathVariable("filterId") final Long filterId);
/**
* Handles the POST request for changing distribution set for auto
* assignment of a target filter.
*
* @param filterId
* of the target to change
* @param dsId
* of the Id of the auto assign distribution set
* @return http status
*/
@RequestMapping(method = RequestMethod.POST, value = "/{filterId}/autoAssignDS", consumes = {
"application/hal+json",
MediaType.APPLICATION_JSON_VALUE }, produces = { "application/hal+json", MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<MgmtTargetFilterQuery> postAssignedDistributionSet(@PathVariable("filterId") final Long filterId,
@RequestBody final MgmtId dsId);
/**
* Handles the DELETE request for removing the distribution set for auto
* assignment of a target filter.
*
* @param filterId
* of the target to change
* @return http status
*/
@RequestMapping(method = RequestMethod.DELETE, value = "/{filterId}/autoAssignDS")
ResponseEntity<Void> deleteAssignedDistributionSet(@PathVariable("filterId") final Long filterId);
}

View File

@@ -23,6 +23,7 @@ import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtTargetAssignmentR
import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.MgmtSoftwareModule;
import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.MgmtSoftwareModuleAssigment;
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTarget;
import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQuery;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtDistributionSetRestApi;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.repository.DeploymentManagement;
@@ -32,12 +33,14 @@ import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.OffsetBasedPageRequest;
import org.eclipse.hawkbit.repository.SoftwareManagement;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.repository.model.TargetWithActionType;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.slf4j.Logger;
@@ -66,6 +69,9 @@ public class MgmtDistributionSetResource implements MgmtDistributionSetRestApi {
@Autowired
private TargetManagement targetManagement;
@Autowired
private TargetFilterQueryManagement targetFilterQueryManagement;
@Autowired
private DeploymentManagement deployManagament;
@@ -223,6 +229,37 @@ public class MgmtDistributionSetResource implements MgmtDistributionSetRestApi {
HttpStatus.OK);
}
@Override
public ResponseEntity<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) {
// check if distribution set exists otherwise throw exception
// immediately
DistributionSet distributionSet = findDistributionSetWithExceptionIfNotFound(distributionSetId);
final int sanitizedOffsetParam = PagingUtility.sanitizeOffsetParam(pagingOffsetParam);
final int sanitizedLimitParam = PagingUtility.sanitizePageLimitParam(pagingLimitParam);
final Sort sorting = PagingUtility.sanitizeTargetSortParam(sortParam);
final Pageable pageable = new OffsetBasedPageRequest(sanitizedOffsetParam, sanitizedLimitParam, sorting);
final Page<TargetFilterQuery> targetFilterQueries;
if (rsqlParam != null) {
targetFilterQueries = this.targetFilterQueryManagement.findTargetFilterQueryByAutoAssignDS(pageable,
distributionSet, rsqlParam);
} else {
targetFilterQueries = this.targetFilterQueryManagement.findTargetFilterQueryByAutoAssignDS(pageable,
distributionSet);
}
return new ResponseEntity<>(
new PagedList<>(MgmtTargetFilterQueryMapper.toResponse(targetFilterQueries.getContent()),
targetFilterQueries.getTotalElements()),
HttpStatus.OK);
}
@Override
public ResponseEntity<MgmtTargetAssignmentResponseBody> createAssignedTarget(
@PathVariable("distributionSetId") final Long distributionSetId,

View File

@@ -0,0 +1,95 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
* <p>
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.rest.resource;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQuery;
import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQueryRequestBody;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetFilterQueryRestApi;
import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
/**
* A mapper which maps repository model to RESTful model representation and
* back.
*
*/
public final class MgmtTargetFilterQueryMapper {
private MgmtTargetFilterQueryMapper() {
// Utility class
}
/**
* Create a response for targets.
*
* @param filters
* list of targets
* @return the response
*/
public static List<MgmtTargetFilterQuery> toResponse(final Iterable<TargetFilterQuery> filters) {
final List<MgmtTargetFilterQuery> mappedList = new ArrayList<>();
if (filters != null) {
for (final TargetFilterQuery filter : filters) {
final MgmtTargetFilterQuery response = toResponse(filter);
mappedList.add(response);
}
}
return mappedList;
}
/**
* Create a response for target.
*
* @param filter
* the target
* @return the response
*/
public static MgmtTargetFilterQuery toResponse(final TargetFilterQuery filter) {
if (filter == null) {
return null;
}
final MgmtTargetFilterQuery targetRest = new MgmtTargetFilterQuery();
targetRest.setFilterId(filter.getId());
targetRest.setName(filter.getName());
targetRest.setQuery(filter.getQuery());
targetRest.setCreatedBy(filter.getCreatedBy());
targetRest.setLastModifiedBy(filter.getLastModifiedBy());
targetRest.setCreatedAt(filter.getCreatedAt());
targetRest.setLastModifiedAt(filter.getLastModifiedAt());
DistributionSet distributionSet = filter.getAutoAssignDistributionSet();
if (distributionSet != null) {
targetRest.setAutoAssignDistributionSet(distributionSet.getId());
}
targetRest.add(linkTo(methodOn(MgmtTargetFilterQueryRestApi.class).getFilter(filter.getId())).withRel("self"));
targetRest.add(linkTo(methodOn(MgmtTargetFilterQueryRestApi.class).postAssignedDistributionSet(filter.getId(),null)).withRel("autoAssignDS"));
return targetRest;
}
static TargetFilterQuery fromRequest(final EntityFactory entityFactory,
final MgmtTargetFilterQueryRequestBody filterRest) {
final TargetFilterQuery filter = entityFactory.generateTargetFilterQuery();
filter.setName(filterRest.getName());
filter.setQuery(filterRest.getQuery());
return filter;
}
}

View File

@@ -0,0 +1,180 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
* <p>
* 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.sanitizeTargetSortParam(sortParam);
final Pageable pageable = new OffsetBasedPageRequest(sanitizedOffsetParam, sanitizedLimitParam, sorting);
final Slice<TargetFilterQuery> findTargetFiltersAll;
final Long countTargetsAll;
if (rsqlParam != null) {
final Page<TargetFilterQuery> findFilterPage = this.filterManagement
.findTargetFilterQueryByFilter(pageable, rsqlParam);
countTargetsAll = findFilterPage.getTotalElements();
findTargetFiltersAll = findFilterPage;
} else {
findTargetFiltersAll = this.filterManagement.findAllTargetFilterQuery(pageable);
countTargetsAll = this.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 = this.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 = this.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);
this.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 = this.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;
if (distributionSetRest == null) {
retStatus = HttpStatus.NO_CONTENT;
} else {
retStatus = HttpStatus.OK;
}
return new ResponseEntity<>(distributionSetRest, retStatus);
}
@Override
public ResponseEntity<Void> deleteAssignedDistributionSet(@PathVariable("filterId") Long filterId) {
final TargetFilterQuery filter = findFilterWithExceptionIfNotFound(filterId);
filter.setAutoAssignDistributionSet(null);
this.filterManagement.updateTargetFilterQuery(filter);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
private TargetFilterQuery findFilterWithExceptionIfNotFound(final Long filterId) {
final TargetFilterQuery filter = this.filterManagement.findTargetFilterQueryById(filterId);
if (filter == null) {
throw new EntityNotFoundException("TargetFilterQuery with Id {" + filterId + "} does not exist");
}
return filter;
}
}

View File

@@ -30,11 +30,8 @@ import java.util.Set;
import org.apache.commons.lang3.RandomStringUtils;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.model.*;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.test.util.TestdataFactory;
import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.eclipse.hawkbit.rest.AbstractRestIntegrationTest;
@@ -297,6 +294,25 @@ public class MgmtDistributionSetResourceTest extends AbstractRestIntegrationTest
.andExpect(jsonPath("$.content[0].controllerId", equalTo(knownTargetId)));
}
@Test
@Description("Ensures that target filters with auto assign DS are returned as persisted in the repository.")
public void getAutoAssignTargetFiltersOfDistributionSet() throws Exception {
// prepare distribution set
final String knownFilterName = "a";
final Set<DistributionSet> createDistributionSetsAlphabetical = createDistributionSetsAlphabetical(1);
final DistributionSet createdDs = createDistributionSetsAlphabetical.iterator().next();
final TargetFilterQuery tfq = targetFilterQueryManagement
.createTargetFilterQuery(entityFactory.generateTargetFilterQuery(knownFilterName, "x==y", createdDs));
// create some dummy targets which are not assigned or installed
targetFilterQueryManagement.createTargetFilterQuery(entityFactory.generateTargetFilterQuery("b", "x==y"));
targetFilterQueryManagement.createTargetFilterQuery(entityFactory.generateTargetFilterQuery("c", "x==y"));
mvc.perform(get(MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING + "/" + createdDs.getId()
+ "/autoAssignTargetFilters")).andExpect(status().isOk()).andExpect(jsonPath("$.size", equalTo(1)))
.andExpect(jsonPath("$.content[0].name", equalTo(knownFilterName)));
}
@Test
@Description("Ensures that DS in repository are listed with proper paging properties.")
public void getDistributionSetsWithoutAddtionalRequestParameters() throws Exception {

View File

@@ -0,0 +1,362 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.rest.resource;
import static com.google.common.collect.Lists.newArrayList;
import static org.fest.assertions.api.Assertions.assertThat;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.Matchers.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import java.util.*;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.exception.SpServerError;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.model.*;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.rest.AbstractRestIntegrationTest;
import org.eclipse.hawkbit.rest.exception.MessageNotReadableException;
import org.eclipse.hawkbit.rest.json.model.ExceptionInfo;
import org.eclipse.hawkbit.rest.util.JsonBuilder;
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
import org.json.JSONObject;
import org.junit.Test;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Slice;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MvcResult;
import com.jayway.jsonpath.JsonPath;
import ru.yandex.qatools.allure.annotations.Description;
import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Stories;
/**
* Spring MVC Tests against the MgmtTargetResource.
*
*/
@Features("Component Tests - Management API")
@Stories("Target Filter Query Resource")
public class MgmtTargetFilterQueryResourceTest extends AbstractRestIntegrationTest {
private static final String TARGET_DESCRIPTION_TEST = "created in test";
private static final String JSON_PATH_ROOT = "$";
// fields, attributes
private static final String JSON_PATH_FIELD_ID = ".id";
private static final String JSON_PATH_FIELD_NAME = ".name";
private static final String JSON_PATH_FIELD_QUERY = ".query";
private static final String JSON_PATH_FIELD_CONTENT = ".content";
private static final String JSON_PATH_FIELD_SIZE = ".size";
private static final String JSON_PATH_FIELD_TOTAL = ".total";
private static final String JSON_PATH_FIELD_AUTO_ASSIGN_DS = ".autoAssignDistributionSet";
// target
// $.field
static final String JSON_PATH_PAGED_LIST_CONTENT = JSON_PATH_ROOT + JSON_PATH_FIELD_CONTENT;
static final String JSON_PATH_PAGED_LIST_SIZE = JSON_PATH_ROOT + JSON_PATH_FIELD_SIZE;
static final String JSON_PATH_PAGED_LIST_TOTAL = JSON_PATH_ROOT + JSON_PATH_FIELD_TOTAL;
private static final String JSON_PATH_NAME = JSON_PATH_ROOT + JSON_PATH_FIELD_NAME;
private static final String JSON_PATH_ID = JSON_PATH_ROOT + JSON_PATH_FIELD_ID;
private static final String JSON_PATH_QUERY = JSON_PATH_ROOT + JSON_PATH_FIELD_QUERY;
private static final String JSON_PATH_AUTO_ASSIGN_DS = JSON_PATH_ROOT + JSON_PATH_FIELD_AUTO_ASSIGN_DS;
@Test
@Description("Ensures that deletion is executed if permitted.")
public void deleteTargetFilterQueryReturnsOK() throws Exception {
final String filterName = "filter_01";
TargetFilterQuery filterQuery = createSingleTargetFilterQuery(filterName, "name=test_01");
mvc.perform(delete(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + filterQuery.getId()))
.andExpect(status().isOk());
TargetFilterQuery tfq = targetFilterQueryManagement.findTargetFilterQueryById(filterQuery.getId());
assertThat(tfq).isNull();
}
@Test
@Description("Ensures that deletion is refused with not found if target does not exist.")
public void deleteTargetWhichDoesNotExistsLeadsToEntityNotFound() throws Exception {
final String notExistingId = "4395";
mvc.perform(delete(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + notExistingId))
.andExpect(status().isNotFound());
}
@Test
@Description("Ensures that update is refused with not found if target does not exist.")
public void updateTargetWhichDoesNotExistsLeadsToEntityNotFound() throws Exception {
final String notExistingId = "4395";
mvc.perform(put(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + notExistingId).content("{}")
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isNotFound());
}
@Test
@Description("Ensures that update request is reflected by repository.")
public void updateTargetFilterQueryQuery() throws Exception {
final String filterName = "filter_02";
final String filterQuery = "name=test_02";
final String filterQuery2 = "name=test_02_changed";
final String body = new JSONObject().put("query", filterQuery2).toString();
// prepare
TargetFilterQuery tfq = createSingleTargetFilterQuery(filterName, filterQuery);
mvc.perform(put(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId()).content(body)
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$.id", equalTo(tfq.getId().intValue())))
.andExpect(jsonPath("$.query", equalTo(filterQuery2)))
.andExpect(jsonPath("$.name", equalTo(filterName)));
TargetFilterQuery tfqCheck = targetFilterQueryManagement.findTargetFilterQueryById(tfq.getId());
assertThat(tfqCheck.getQuery()).isEqualTo(filterQuery2);
assertThat(tfqCheck.getName()).isEqualTo(filterName);
}
@Test
@Description("Ensures that update request is reflected by repository.")
public void updateTargetFilterQueryName() throws Exception {
final String filterName = "filter_03";
final String filterName2 = "filter_03_changed";
final String filterQuery = "name=test_03";
final String body = new JSONObject().put("name", filterName2).toString();
// prepare
TargetFilterQuery tfq = entityFactory.generateTargetFilterQuery();
tfq.setName(filterName);
tfq.setQuery(filterQuery);
tfq = targetFilterQueryManagement.createTargetFilterQuery(tfq);
mvc.perform(put(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId()).content(body)
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$.id", equalTo(tfq.getId().intValue())))
.andExpect(jsonPath("$.query", equalTo(filterQuery)))
.andExpect(jsonPath("$.name", equalTo(filterName2)));
TargetFilterQuery tfqCheck = targetFilterQueryManagement.findTargetFilterQueryById(tfq.getId());
assertThat(tfqCheck.getQuery()).isEqualTo(filterQuery);
assertThat(tfqCheck.getName()).isEqualTo(filterName2);
}
@Test
@Description("Ensures that request returns list of filters in defined format.")
public void getTargetFilterQueryWithoutAdditionalRequestParameters() throws Exception {
final int knownTargetAmount = 3;
final String idA = "a";
final String idB = "b";
final String idC = "c";
final String testQuery = "name=test";
createSingleTargetFilterQuery(idA, testQuery);
createSingleTargetFilterQuery(idB, testQuery);
createSingleTargetFilterQuery(idC, testQuery);
mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING)).andExpect(status().isOk())
.andDo(MockMvcResultPrinter.print())
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_TOTAL, equalTo(knownTargetAmount)))
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_SIZE, equalTo(knownTargetAmount)))
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_CONTENT, hasSize(knownTargetAmount)))
// idA
.andExpect(jsonPath("$.content.[?(@.name==" + idA + ")].name", contains(idA)))
.andExpect(jsonPath("$.content.[?(@.name==" + idA + ")].query", contains(testQuery)))
// idB
.andExpect(jsonPath("$.content.[?(@.name==" + idB + ")].name", contains(idB)))
.andExpect(jsonPath("$.content.[?(@.name==" + idB + ")].query", contains(testQuery)))
// idC
.andExpect(jsonPath("$.content.[?(@.name==" + idC + ")].name", contains(idC)))
.andExpect(jsonPath("$.content.[?(@.name==" + idC + ")].query", contains(testQuery)));
}
@Test
@Description("Ensures that request returns list of filters in defined format in size reduced by given limit parameter.")
public void getTargetWithPagingLimitRequestParameter() throws Exception {
final int limitSize = 1;
final int knownTargetAmount = 3;
final String idA = "a";
final String idB = "b";
final String idC = "c";
final String testQuery = "name=test";
createSingleTargetFilterQuery(idA, testQuery);
createSingleTargetFilterQuery(idB, testQuery);
createSingleTargetFilterQuery(idC, testQuery);
mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING)
.param(MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, String.valueOf(limitSize)))
.andExpect(status().isOk()).andDo(MockMvcResultPrinter.print())
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_TOTAL, equalTo(knownTargetAmount)))
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_SIZE, equalTo(limitSize)))
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_CONTENT, hasSize(limitSize)))
// idA
.andExpect(jsonPath("$.content.[?(@.name==" + idA + ")].name", contains(idA)))
.andExpect(jsonPath("$.content.[?(@.name==" + idA + ")].query", contains(testQuery)));
}
@Test
@Description("Ensures that request returns list of filters in defined format in size reduced by given limit and offset parameter.")
public void getTargetWithPagingLimitAndOffsetRequestParameter() throws Exception {
final int knownTargetAmount = 5;
final int offsetParam = 2;
final int expectedSize = knownTargetAmount - offsetParam;
final String idC = "c";
final String idD = "d";
final String idE = "e";
final String testQuery = "name=test";
createSingleTargetFilterQuery("a", testQuery);
createSingleTargetFilterQuery("b", testQuery);
createSingleTargetFilterQuery(idC, testQuery);
createSingleTargetFilterQuery(idD, testQuery);
createSingleTargetFilterQuery(idE, testQuery);
mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING)
.param(MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, String.valueOf(offsetParam))
.param(MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, String.valueOf(knownTargetAmount)))
.andExpect(status().isOk()).andDo(MockMvcResultPrinter.print())
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_TOTAL, equalTo(knownTargetAmount)))
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_SIZE, equalTo(expectedSize)))
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_CONTENT, hasSize(expectedSize)))
// idA
.andExpect(jsonPath("$.content.[?(@.name==" + idC + ")].name", contains(idC)))
.andExpect(jsonPath("$.content.[?(@.name==" + idC + ")].query", contains(testQuery)))
// idB
.andExpect(jsonPath("$.content.[?(@.name==" + idD + ")].name", contains(idD)))
.andExpect(jsonPath("$.content.[?(@.name==" + idD + ")].query", contains(testQuery)))
// idC
.andExpect(jsonPath("$.content.[?(@.name==" + idE + ")].name", contains(idE)))
.andExpect(jsonPath("$.content.[?(@.name==" + idE + ")].query", contains(testQuery)));
}
@Test
public void getSingleTarget() throws Exception {
// create first a target which can be retrieved by rest interface
final String knownQuery = "name=test01";
final String knownName = "someName";
final TargetFilterQuery tfq = createSingleTargetFilterQuery(knownName, knownQuery);
final String hrefPrefix = "http://localhost"+MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING+"/" + tfq.getId();
// test
mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath(JSON_PATH_NAME, equalTo(knownName)))
.andExpect(jsonPath(JSON_PATH_QUERY, equalTo(knownQuery)))
.andExpect(jsonPath("$._links.self.href", equalTo(hrefPrefix)))
.andExpect(jsonPath("$._links.autoAssignDS.href", equalTo(hrefPrefix + "/autoAssignDS")));
}
@Test
public void getSingleTargetNoExistsResponseNotFound() throws Exception {
final String targetIdNotExists = "546546";
// test
final MvcResult mvcResult = mvc
.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + targetIdNotExists))
.andExpect(status().isNotFound()).andReturn();
// verify response json exception message
final ExceptionInfo exceptionInfo = ResourceUtility
.convertException(mvcResult.getResponse().getContentAsString());
assertThat(exceptionInfo.getErrorCode()).isEqualTo(SpServerError.SP_REPO_ENTITY_NOT_EXISTS.getKey());
}
@Test
public void createTargetFilterQueryWithBadPayloadBadRequest() throws Exception {
final String notJson = "abc";
final MvcResult mvcResult = mvc
.perform(post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING).content(notJson)
.contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest()).andReturn();
assertThat(targetFilterQueryManagement.countAllTargetFilterQuery()).isEqualTo(0);
// verify response json exception message
final ExceptionInfo exceptionInfo = ResourceUtility
.convertException(mvcResult.getResponse().getContentAsString());
assertThat(exceptionInfo.getExceptionClass()).isEqualTo(MessageNotReadableException.class.getName());
assertThat(exceptionInfo.getErrorCode()).isEqualTo(SpServerError.SP_REST_BODY_NOT_READABLE.getKey());
}
@Test
public void setAutoAssignDistributionSetToTargetFilterQuery() throws Exception {
final String knownQuery = "name=test05";
final String knownName = "filter05";
final DistributionSet set = testdataFactory.createDistributionSet("one");
final TargetFilterQuery tfq = createSingleTargetFilterQuery(knownName, knownQuery);
mvc.perform(post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/"+tfq.getId()+"/autoAssignDS")
.content("{\"id\":" + set.getId() + "}").contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
assertThat(targetFilterQueryManagement.findTargetFilterQueryById(tfq.getId()).getAutoAssignDistributionSet()).isEqualTo(set);
final String hrefPrefix = "http://localhost"+MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING+"/" + tfq.getId();
mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath(JSON_PATH_NAME, equalTo(knownName)))
.andExpect(jsonPath(JSON_PATH_QUERY, equalTo(knownQuery)))
.andExpect(jsonPath(JSON_PATH_AUTO_ASSIGN_DS, equalTo(set.getId().intValue())))
.andExpect(jsonPath("$._links.self.href", equalTo(hrefPrefix)))
.andExpect(jsonPath("$._links.autoAssignDS.href", equalTo(hrefPrefix + "/autoAssignDS")));
}
@Test
public void deleteAutoAssignDistributionSetOfTargetFilterQuery() throws Exception {
final String knownQuery = "name=test06";
final String knownName = "filter06";
final String dsName = "testDS";
final DistributionSet set = testdataFactory.createDistributionSet(dsName);
TargetFilterQuery tfq = entityFactory.generateTargetFilterQuery();
tfq.setName(knownName);
tfq.setQuery(knownQuery);
tfq.setAutoAssignDistributionSet(set);
tfq = targetFilterQueryManagement.createTargetFilterQuery(tfq);
assertThat(targetFilterQueryManagement.findTargetFilterQueryById(tfq.getId()).getAutoAssignDistributionSet())
.isEqualTo(set);
mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/"+tfq.getId()+"/autoAssignDS"))
.andExpect(status().isOk())
.andExpect(jsonPath(JSON_PATH_NAME, equalTo(dsName)));
mvc.perform(delete(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/"+tfq.getId()+"/autoAssignDS"))
.andExpect(status().isNoContent());
assertThat(targetFilterQueryManagement.findTargetFilterQueryById(tfq.getId()).getAutoAssignDistributionSet())
.isNull();
mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/"+tfq.getId()+"/autoAssignDS"))
.andExpect(status().isNoContent());
}
private TargetFilterQuery createSingleTargetFilterQuery(final String name, final String query) {
final TargetFilterQuery target = entityFactory.generateTargetFilterQuery();
target.setName(name);
target.setQuery(query);
return targetFilterQueryManagement.createTargetFilterQuery(target);
}
}

View File

@@ -0,0 +1,50 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* Rollout Management properties.
*
*/
@Component
@ConfigurationProperties("hawkbit.autoassign")
public class AutoAssignProperties {
/**
* Autoassign scheduler configuration.
*/
public static class Scheduler {
// used by @Scheduled annotation which needs constant
public static final String PROP_SCHEDULER_DELAY_PLACEHOLDER = "${hawkbit.autoassign.scheduler.fixedDelay:60000}";
/**
* Schedule where the autoassign scheduler looks necessary state changes in
* milliseconds.
*/
private long fixedDelay = 60000L;
public long getFixedDelay() {
return fixedDelay;
}
public void setFixedDelay(final long fixedDelay) {
this.fixedDelay = fixedDelay;
}
}
private final Scheduler scheduler = new Scheduler();
public Scheduler getScheduler() {
return scheduler;
}
}

View File

@@ -104,6 +104,27 @@ public interface DeploymentManagement {
DistributionSetAssignmentResult assignDistributionSet(@NotNull Long dsID,
@NotEmpty Collection<TargetWithActionType> targets);
/**
* method assigns the {@link DistributionSet} to all {@link Target}s by
* their IDs with a specific {@link ActionType} and an action message.
*
* @param dsID
* the ID of the distribution set to assign
* @param targets
* a list of all targets and their action type
* @param actionMessage
* an optional message for the action status
* @return the assignment result
*
* @throw IncompleteDistributionSetException if mandatory
* {@link SoftwareModuleType} are not assigned as define by the
* {@link DistributionSetType}.
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
DistributionSetAssignmentResult assignDistributionSet(@NotNull Long dsID,
@NotEmpty Collection<TargetWithActionType> targets,
String actionMessage);
/**
* method assigns the {@link DistributionSet} to all {@link Target}s by
* their IDs with a specific {@link ActionType} and {@code forcetime}.
@@ -364,9 +385,6 @@ public interface DeploymentManagement {
* pagination parameter
* @param action
* to be filtered on
* @param withMessages
* to <code>true</code> if {@link ActionStatus#getMessages()}
* need to be fetched.
* @return the corresponding {@link Page} of {@link ActionStatus}
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
@@ -463,8 +481,6 @@ public interface DeploymentManagement {
*
* @param action
* to be canceled
* @param target
* for which the action needs cancellation
*
* @return generated {@link Action} or <code>null</code> if not active on
* {@link Target}.
@@ -478,8 +494,6 @@ public interface DeploymentManagement {
* Updates a {@link Action} and forces the {@link Action} if it's not
* already forced.
*
* @param targetId
* the ID of the target
* @param actionId
* the ID of the action
* @return the updated or the found {@link Action}

View File

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

View File

@@ -13,6 +13,7 @@ import javax.validation.constraints.NotNull;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
@@ -70,6 +71,13 @@ public interface TargetFilterQueryManagement {
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Page<TargetFilterQuery> findAllTargetFilterQuery(@NotNull Pageable pageable);
/**
* Counts all target filter queries
* @return the number of all target filter queries
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Long countAllTargetFilterQuery();
/**
* Retrieves all target filter query which {@link TargetFilterQuery}.
*
@@ -77,11 +85,64 @@ public interface TargetFilterQueryManagement {
* @param pageable
* pagination parameter
* @param name
* target filter query name
* name filter
* @return the page with the found {@link TargetFilterQuery}
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Page<TargetFilterQuery> findTargetFilterQueryByFilters(@NotNull Pageable pageable, String name);
Page<TargetFilterQuery> findTargetFilterQueryByName(@NotNull Pageable pageable, String name);
/**
* Retrieves all target filter query which {@link TargetFilterQuery}.
*
*
* @param pageable
* pagination parameter
* @param rsqlFilter
* RSQL filter string
* @return the page with the found {@link TargetFilterQuery}
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Page<TargetFilterQuery> findTargetFilterQueryByFilter(@NotNull Pageable pageable, String rsqlFilter);
/**
* Retrieves all target filter query which {@link TargetFilterQuery}.
*
*
* @param pageable
* pagination parameter
* @param distributionSet
* the auto assign distribution set
* @return the page with the found {@link TargetFilterQuery}
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Page<TargetFilterQuery> findTargetFilterQueryByAutoAssignDS(@NotNull Pageable pageable,
DistributionSet distributionSet);
/**
* Retrieves all target filter query which {@link TargetFilterQuery}.
*
*
* @param pageable
* pagination parameter
* @param distributionSet
* the auto assign distribution set
* @param rsqlParam
* RSQL filter
* @return the page with the found {@link TargetFilterQuery}
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Page<TargetFilterQuery> findTargetFilterQueryByAutoAssignDS(@NotNull Pageable pageable,
DistributionSet distributionSet, String rsqlParam);
/**
* Retrieves all target filter query with auto assign DS which {@link TargetFilterQuery}.
*
*
* @return the page with the found {@link TargetFilterQuery}
* @param pageable
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Page<TargetFilterQuery> findTargetFilterQueryWithAutoAssignDS(@NotNull Pageable pageable);
/**
* Find target filter query by id.

View File

@@ -242,6 +242,35 @@ 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 assigned distribution set.
* and returns not the full target but {@link TargetIdName}.
*
* @param pageRequest
* the pageRequest to enhance the query for paging and sorting
* @param targetFilterQuery
* {@link TargetFilterQuery}
* @return the found {@link TargetIdName}s
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Page<Target> findAllTargetIdsByTargetFilterQueryAndNonDS(@NotNull Pageable pageRequest,
Long distributionSetId,
@NotNull TargetFilterQuery targetFilterQuery);
/**
* Counts all targets for all the given parameter {@link TargetFilterQuery}
* and that don't have the specified assigned distribution set. and returns
* not the full target but {@link TargetIdName}.
*
* @param targetFilterQuery
* {@link TargetFilterQuery}
* @return the found {@link TargetIdName}s
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Long countTargetByTargetFilterQueryAndNonDS(Long distributionSetId,
@NotNull TargetFilterQuery targetFilterQuery);
/**
* retrieves {@link Target}s by the assigned {@link DistributionSet} without
* details, i.e. NO {@link Target#getTags()} and {@link Target#getActions()}

View File

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

View File

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

View File

@@ -175,13 +175,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
@@ -196,21 +206,23 @@ public class JpaDeploymentManagement implements DeploymentManagement {
String.format("no %s with id %d found", DistributionSet.class.getSimpleName(), dsID));
}
return assignDistributionSetToTargets(set, targets, (JpaRollout) rollout, (JpaRolloutGroup) rolloutGroup);
return assignDistributionSetToTargets(set, targets, (JpaRollout) rollout, (JpaRolloutGroup) rolloutGroup, null);
}
/**
* method assigns the {@link DistributionSet} to all {@link Target}s by
* their IDs with a specific {@link ActionType} and {@code forcetime}.
*
* @param dsID
* @param set
* the ID of the distribution set to assign
* @param targets
* @param targetsWithActionType
* a list of all targets and their action type
* @param rollout
* the rollout for this assignment
* @param rolloutGroup
* the rollout group for this assignment
* @param actionMessage
* an optional message to be written into the action status
* @return the assignment result
*
* @throw IncompleteDistributionSetException if mandatory
@@ -219,7 +231,7 @@ public class JpaDeploymentManagement implements DeploymentManagement {
*/
private DistributionSetAssignmentResult assignDistributionSetToTargets(@NotNull final JpaDistributionSet set,
final Collection<TargetWithActionType> targetsWithActionType, final JpaRollout rollout,
final JpaRolloutGroup rolloutGroup) {
final JpaRolloutGroup rolloutGroup, final String actionMessage) {
if (!set.isComplete()) {
throw new IncompleteDistributionSetException(
@@ -292,6 +304,9 @@ public class JpaDeploymentManagement implements DeploymentManagement {
actionStatus.setAction(action);
actionStatus.setOccurredAt(action.getCreatedAt());
actionStatus.setStatus(Status.RUNNING);
if(actionMessage != null) {
actionStatus.addMessage(actionMessage);
}
actionStatusRepository.save(actionStatus);
});
@@ -346,7 +361,7 @@ public class JpaDeploymentManagement implements DeploymentManagement {
* the Target which has been assigned to a distribution set
* @param actionId
* the action id of the assignment
* @param softwareModules
* @param modules
* the software modules which have been assigned
*/
private void assignDistributionSetEvent(final JpaTarget target, final Long actionId,
@@ -364,11 +379,11 @@ public class JpaDeploymentManagement implements DeploymentManagement {
}
/**
* Removes {@link UpdateAction}s that are no longer necessary and sends
* Removes {@link Action}s that are no longer necessary and sends
* cancellations to the controller.
*
* @param myTarget
* to override {@link UpdateAction}s
* @param targetsIds
* to override {@link Action}s
*/
private Set<Long> overrideObsoleteUpdateActions(final List<Long> targetsIds) {
@@ -403,7 +418,7 @@ public class JpaDeploymentManagement implements DeploymentManagement {
return assignDistributionSetToTargets(set, tIDs.stream()
.map(t -> new TargetWithActionType(t, actionType, forcedTime)).collect(Collectors.toList()), null,
null);
null, null);
}
@Override

View File

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

View File

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

View File

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

View File

@@ -12,12 +12,14 @@ import java.util.ArrayList;
import java.util.List;
import org.eclipse.hawkbit.repository.TargetFields;
import org.eclipse.hawkbit.repository.TargetFilterQueryFields;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetFilterQuerySpecification;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
@@ -33,6 +35,8 @@ import org.springframework.validation.annotation.Validated;
import com.google.common.base.Strings;
import javax.validation.constraints.NotNull;
/**
* JPA implementation of {@link TargetFilterQueryManagement}.
*
@@ -67,13 +71,18 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
return convertPage(targetFilterQueryRepository.findAll(pageable), pageable);
}
@Override
public Long countAllTargetFilterQuery() {
return targetFilterQueryRepository.count();
}
private static Page<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) {
public Page<TargetFilterQuery> findTargetFilterQueryByName(final Pageable pageable, final String name) {
final List<Specification<JpaTargetFilterQuery>> specList = new ArrayList<>();
if (!Strings.isNullOrEmpty(name)) {
specList.add(TargetFilterQuerySpecification.likeName(name));
@@ -81,6 +90,41 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
return convertPage(findTargetFilterQueryByCriteriaAPI(pageable, specList), pageable);
}
@Override
public Page<TargetFilterQuery> findTargetFilterQueryByFilter(@NotNull Pageable pageable, String rsqlFilter) {
final List<Specification<JpaTargetFilterQuery>> specList = new ArrayList<>();
if (!Strings.isNullOrEmpty(rsqlFilter)) {
specList.add(RSQLUtility.parse(rsqlFilter, TargetFilterQueryFields.class));
}
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<>();
if (distributionSet != null) {
specList.add(TargetFilterQuerySpecification.byAutoAssignDS(distributionSet));
}
if (!Strings.isNullOrEmpty(rsqlFilter)) {
specList.add(RSQLUtility.parse(rsqlFilter, TargetFilterQueryFields.class));
}
return convertPage(findTargetFilterQueryByCriteriaAPI(pageable, specList), pageable);
}
@Override
public Page<TargetFilterQuery> findTargetFilterQueryWithAutoAssignDS(@NotNull Pageable pageable) {
final List<Specification<JpaTargetFilterQuery>> specList = new ArrayList<>();
specList.add(TargetFilterQuerySpecification.withAutoAssignDS());
return convertPage(findTargetFilterQueryByCriteriaAPI(pageable, specList), pageable);
}
private Page<JpaTargetFilterQuery> findTargetFilterQueryByCriteriaAPI(final Pageable pageable,
final List<Specification<JpaTargetFilterQuery>> specList) {
if (specList == null || specList.isEmpty()) {

View File

@@ -26,6 +26,7 @@ import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.validation.constraints.NotNull;
import org.eclipse.hawkbit.repository.TargetFields;
import org.eclipse.hawkbit.repository.TargetManagement;
@@ -561,6 +562,31 @@ public class JpaTargetManagement implements TargetManagement {
.collect(Collectors.toList());
}
@Override
public Page<Target> findAllTargetIdsByTargetFilterQueryAndNonDS(@NotNull Pageable pageRequest,
Long distributionSetId, @NotNull TargetFilterQuery targetFilterQuery) {
final Specification<JpaTarget> spec = RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class);
return findTargetsBySpec(
(root, cq,
cb) -> cb.and(spec.toPredicate(root, cq, cb), TargetSpecifications
.hasNotDistributionSetInActions(distributionSetId).toPredicate(root, cq, cb)),
pageRequest);
}
@Override
public Long countTargetByTargetFilterQueryAndNonDS(Long distributionSetId, @NotNull TargetFilterQuery targetFilterQuery) {
final Specification<JpaTarget> spec = RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class);
final List<Specification<JpaTarget>> specList = new ArrayList<>();
specList.add(spec);
specList.add(TargetSpecifications.hasNotDistributionSetInActions(distributionSetId));
return countByCriteriaAPI(specList);
}
@PreDestroy
void destroy() {
eventBus.unregister(this);

View File

@@ -13,6 +13,8 @@ import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
@@ -43,4 +45,15 @@ public interface TargetFilterQueryRepository
@Transactional
<S extends JpaTargetFilterQuery> S save(S entity);
/**
* Sets the auto assign distribution sets to null which match the ds ids.
*
* @param dsIds
* distribution set ids to be set to null
*/
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Query("update JpaTargetFilterQuery d set d.autoAssignDistributionSet = NULL where d.autoAssignDistributionSet in :ids")
void unsetAutoAssignDistributionSet(@Param("ids") Long... dsIds);
}

View File

@@ -0,0 +1,168 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.jpa.autoassign;
import java.util.List;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.exception.AbstractServerRtException;
import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.RepositoryModelConstants;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.repository.model.TargetWithActionType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionTemplate;
import javax.persistence.PersistenceException;
/**
* Checks if targets need a new distribution set (DS) based on the target filter queries and
* assigns the new DS when necessary.
* First all target filter queries are listed. For every target filter query (TFQ) the auto assign DS
* is retrieved.
* All targets get listed per target filter query, that match the TFQ and that don't have the
* auto assign DS in their action history.
*/
@Component
public class AutoAssignChecker {
private static final Logger LOGGER = LoggerFactory.getLogger(AutoAssignChecker.class);
@Autowired
private TargetFilterQueryManagement targetFilterQueryManagement;
@Autowired
private TargetManagement targetManagement;
@Autowired
private DeploymentManagement deploymentManagement;
@Autowired
private PlatformTransactionManager transactionManager;
private TransactionTemplate transactionTemplate;
/**
* Maximum for target filter queries with auto assign DS
* Maximum for targets that are fetched in one turn
*/
private static final int PAGE_SIZE = 1000;
/**
* The message which is added to the action status when a distribution set is assigned
* to an target. First %s is the name of the target filter.
*/
private static final String ACTION_MESSAGE = "Auto assignment by target filter: %s";
/**
* Checks all target filter queries with an auto assign distribution set
* and triggers the check and assignment to targets that don't have the design DS yet
*/
public void check() {
if(transactionTemplate == null) {
final DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setName("autoAssignDSToTargets");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
transactionTemplate = new TransactionTemplate(transactionManager, def);
}
PageRequest pageRequest = new PageRequest(0, PAGE_SIZE);
Page<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.findAllTargetIdsByTargetFilterQueryAndNonDS(new PageRequest(0, count),
dsId, targetFilterQuery);
return targets.getContent().stream()
.map(t -> new TargetWithActionType(t.getControllerId(), Action.ActionType.FORCED,
RepositoryModelConstants.NO_FORCE_TIME))
.collect(Collectors.toList());
}
}

View File

@@ -0,0 +1,77 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.jpa.autoassign;
import java.util.List;
import org.eclipse.hawkbit.repository.AutoAssignProperties;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
/**
* Scheduler to check target filters for auto assignment of distribution sets
*/
@Component
// don't active the auto assign scheduler in test, otherwise it is hard to test
@Profile("!test")
public class AutoAssignScheduler {
private static final Logger LOGGER = LoggerFactory.getLogger(AutoAssignScheduler.class);
@Autowired
private TenantAware tenantAware;
@Autowired
private SystemManagement systemManagement;
@Autowired
private SystemSecurityContext systemSecurityContext;
@Autowired
private AutoAssignChecker autoAssignChecker;
/**
* Scheduler method called by the spring-async mechanism. Retrieves all
* tenants from the {@link SystemManagement#findTenants()} and runs for each
* tenant the auto assignments defined in the target filter queries
* {@link SystemSecurityContext}.
*/
@Scheduled(initialDelayString = AutoAssignProperties.Scheduler.PROP_SCHEDULER_DELAY_PLACEHOLDER, fixedDelayString = AutoAssignProperties.Scheduler.PROP_SCHEDULER_DELAY_PLACEHOLDER)
public void autoAssignScheduler() {
LOGGER.debug("auto assign schedule checker has been triggered.");
// run this code in system code privileged to have the necessary
// permission to query and create entities.
systemSecurityContext.runAsSystem(() -> {
// workaround eclipselink that is currently not possible to
// execute a query without multitenancy if MultiTenant
// annotation is used.
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=355458. So
// iterate through all tenants and execute the rollout check for
// each tenant separately.
final List<String> tenants = systemManagement.findTenants();
LOGGER.info("Checking target filter queries for tenants: {}", tenants.size());
for (final String tenant : tenants) {
tenantAware.runAsTenant(tenant, () -> {
autoAssignChecker.check();
return null;
});
}
return null;
});
}
}

View File

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

View File

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

View File

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

View File

@@ -14,12 +14,15 @@ import java.util.List;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.ListJoin;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.SetJoin;
import javax.validation.constraints.NotNull;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction_;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
@@ -210,6 +213,24 @@ public final class TargetSpecifications {
distributionSetId);
}
/**
* {@link Specification} for retrieving {@link Target}s that don't have the given
* distribution set in their action history
*
* @param distributionSetId
* the ID of the distribution set which must not be assigned
* @return the {@link Target} {@link Specification}
*/
public static Specification<JpaTarget> hasNotDistributionSetInActions(final Long distributionSetId) {
return (targetRoot, query, cb) -> {
final ListJoin<JpaTarget, JpaAction> actionsJoin = targetRoot.join(JpaTarget_.actions, JoinType.LEFT);
actionsJoin.on(cb.equal(actionsJoin.get(JpaAction_.distributionSet).get(JpaDistributionSet_.id),
distributionSetId));
return cb.isNull(actionsJoin.get(JpaAction_.id));
};
}
/**
* {@link Specification} for retrieving {@link Target}s by assigned
* distribution set.

View File

@@ -0,0 +1,8 @@
ALTER TABLE sp_target_filter_query
ADD column auto_assign_distribution_set BIGINT;
ALTER TABLE sp_target_filter_query
ADD CONSTRAINT fk_filter_auto_assign_ds
FOREIGN KEY (auto_assign_distribution_set)
REFERENCES sp_distribution_set
ON DELETE SET NULL;

View File

@@ -0,0 +1,8 @@
ALTER TABLE sp_target_filter_query
ADD COLUMN auto_assign_distribution_set BIGINT;
ALTER TABLE sp_target_filter_query
ADD CONSTRAINT fk_filter_auto_assign_ds
FOREIGN KEY (auto_assign_distribution_set)
REFERENCES sp_distribution_set (id)
ON DELETE SET NULL;

View File

@@ -8,19 +8,27 @@
*/
package org.eclipse.hawkbit.repository.jpa;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.junit.Test;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import ru.yandex.qatools.allure.annotations.Description;
import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Stories;
import java.util.Iterator;
import java.util.List;
import static org.fest.assertions.api.Assertions.assertThat;
import static org.junit.Assert.*;
/**
* Test class for {@link TargetFilterQueryManagement}.
*
@@ -39,6 +47,22 @@ public class TargetFilterQueryManagenmentTest extends AbstractJpaIntegrationTest
targetFilterQueryManagement.findTargetFilterQueryByName(filterName));
}
@Test
@Description("Test searching a target filter query.")
public void searchTargetFilterQuery() {
final String filterName = "targetFilterQueryName";
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
.createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001"));
targetFilterQueryManagement
.createTargetFilterQuery(new JpaTargetFilterQuery("someOtherFilter", "name==PendingTargets002"));
List<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
@Description("Checks if the EntityAlreadyExistsException is thrown if a targetfilterquery with the same name are created more than once.")
public void createDuplicateTargetFilterQuery() {
@@ -82,4 +106,142 @@ public class TargetFilterQueryManagenmentTest extends AbstractJpaIntegrationTest
}
@Test
@Description("Test assigning a distribution set")
public void assignDistributionSet() {
final String filterName = "target_filter_02";
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
.createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001"));
final DistributionSet distributionSet = distributionSetManagement.createDistributionSet(new JpaDistributionSet(
"dist_Set_01", "0.1", "", null, null
));
targetFilterQuery.setAutoAssignDistributionSet(distributionSet);
targetFilterQueryManagement.updateTargetFilterQuery(targetFilterQuery);
TargetFilterQuery tfq = targetFilterQueryManagement.findTargetFilterQueryByName(filterName);
assertEquals("Returns correct distribution set", distributionSet,
tfq.getAutoAssignDistributionSet());
}
@Test
@Description("Test removing distribution set while it has a relation to a target filter query")
public void removeAssignDistributionSet() {
final String filterName = "target_filter_03";
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
.createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001"));
final DistributionSet distributionSet = distributionSetManagement.createDistributionSet(new JpaDistributionSet(
"dist_Set_02", "0.1", "", null, null
));
targetFilterQuery.setAutoAssignDistributionSet(distributionSet);
targetFilterQueryManagement.updateTargetFilterQuery(targetFilterQuery);
// Check if target filter query is there
TargetFilterQuery tfq = targetFilterQueryManagement.findTargetFilterQueryByName(filterName);
assertEquals("Returns correct distribution set", distributionSet,
tfq.getAutoAssignDistributionSet());
distributionSetManagement.deleteDistributionSet(distributionSet);
// Check if auto assign distribution set is null
tfq = targetFilterQueryManagement.findTargetFilterQueryByName(filterName);
assertNotNull("Returns target filter query", tfq);
assertNull("Returns distribution set as null", tfq.getAutoAssignDistributionSet());
}
@Test
@Description("Test to implicitly remove the auto assign distribution set when the ds is soft deleted")
public void implicitlyRemoveAssignDistributionSet() {
final String filterName = "target_filter_03";
DistributionSet distributionSet = testdataFactory.createDistributionSet("dist_set");
Target target = testdataFactory.createTarget();
// Assign the distribution set to an target, to force a soft delete in a
// later step
deploymentManagement.assignDistributionSet(distributionSet.getId(), target.getControllerId());
targetFilterQueryManagement.createTargetFilterQuery(
new JpaTargetFilterQuery(filterName, "name==PendingTargets001", (JpaDistributionSet) distributionSet));
// Check if target filter query is there with the distribution set
TargetFilterQuery tfq = targetFilterQueryManagement.findTargetFilterQueryByName(filterName);
assertEquals("Returns correct distribution set", distributionSet, tfq.getAutoAssignDistributionSet());
distributionSetManagement.deleteDistributionSet(distributionSet);
// Check if distribution set is still in the database with deleted flag
assertTrue("Distribution set should be deleted",
distributionSetManagement.findDistributionSetById(distributionSet.getId()).isDeleted());
// Check if auto assign distribution set is null
tfq = targetFilterQueryManagement.findTargetFilterQueryByName(filterName);
assertNotNull("Returns target filter query", tfq);
assertNull("Returns distribution set as null", tfq.getAutoAssignDistributionSet());
}
@Test
@Description("Test finding and auto assign distribution set")
public void findFiltersWithDistributionSet() {
final String filterName = "d";
assertEquals(0L, targetFilterQueryManagement.countAllTargetFilterQuery().longValue());
targetFilterQueryManagement.createTargetFilterQuery(new JpaTargetFilterQuery("a", "name==*"));
targetFilterQueryManagement.createTargetFilterQuery(new JpaTargetFilterQuery("b", "name==*"));
final DistributionSet distributionSet = distributionSetManagement
.createDistributionSet(new JpaDistributionSet("dist_Set_01", "0.1", "", null, null));
final DistributionSet distributionSet2 = distributionSetManagement
.createDistributionSet(new JpaDistributionSet("dist_Set_02", "0.1", "", null, null));
final TargetFilterQuery tfq = targetFilterQueryManagement.createTargetFilterQuery(
new JpaTargetFilterQuery("c", "name==x", (JpaDistributionSet) distributionSet));
final TargetFilterQuery tfq2 = targetFilterQueryManagement.createTargetFilterQuery(
new JpaTargetFilterQuery(filterName, "name==z*", (JpaDistributionSet) distributionSet2));
assertEquals(4L, targetFilterQueryManagement.countAllTargetFilterQuery().longValue());
// check if find works
Page<TargetFilterQuery> tfqList = targetFilterQueryManagement
.findTargetFilterQueryByAutoAssignDS(new PageRequest(0, 500), distributionSet);
assertThat(1L).as("Target filter query").isEqualTo(tfqList.getTotalElements());
assertEquals("Returns correct target filter query", tfq.getId(), tfqList.iterator().next().getId());
tfq2.setAutoAssignDistributionSet(distributionSet);
targetFilterQueryManagement.updateTargetFilterQuery(tfq2);
// check if find works for two
tfqList = targetFilterQueryManagement.findTargetFilterQueryByAutoAssignDS(new PageRequest(0, 500),
distributionSet);
assertThat(2L).as("Target filter query count").isEqualTo(tfqList.getTotalElements());
Iterator<TargetFilterQuery> iterator = tfqList.iterator();
assertEquals("Returns correct target filter query 1", tfq.getId(), iterator.next().getId());
assertEquals("Returns correct target filter query 2", tfq2.getId(), iterator.next().getId());
// check if find works with name filter
tfqList = targetFilterQueryManagement.findTargetFilterQueryByAutoAssignDS(new PageRequest(0, 500),
distributionSet, "name==" + filterName);
assertThat(1L).as("Target filter query count").isEqualTo(tfqList.getTotalElements());
assertEquals("Returns correct target filter query", tfq2.getId(), tfqList.iterator().next().getId());
// check if find works for all with auto assign DS
tfqList = targetFilterQueryManagement.findTargetFilterQueryWithAutoAssignDS(new PageRequest(0, 500));
assertThat(2L).as("Target filter query count").isEqualTo(tfqList.getTotalElements());
iterator = tfqList.iterator();
assertEquals("Returns correct target filter query 1", tfq.getId(), iterator.next().getId());
assertEquals("Returns correct target filter query 2", tfq2.getId(), iterator.next().getId());
}
}

View File

@@ -22,16 +22,10 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.*;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetIdName;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
import org.junit.Test;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Slice;
import com.google.common.collect.Lists;
@@ -727,6 +721,26 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
}
@Test
@Description("Verifies that targets without given assigned DS are returned from repository.")
public void findTargetWithoutAssignedDistributionSet() {
final DistributionSet assignedSet = testdataFactory.createDistributionSet("");
final TargetFilterQuery tfq = targetFilterQueryManagement
.createTargetFilterQuery(entityFactory.generateTargetFilterQuery("tfq", "name==*"));
List<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.findAllTargetIdsByTargetFilterQueryAndNonDS(pageReq,
assignedSet.getId(), tfq).getContent();
assertThat(result)
.as("count of targets").hasSize(unassignedTargets.size())
.as("contains all targets").containsAll(unassignedTargets);
}
@Test
@Description("Verfies that targets with given installed DS are returned from repository.")
public void findTargetByInstalledDistributionSet() {

View File

@@ -0,0 +1,157 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.jpa.autoassign;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Slice;
import ru.yandex.qatools.allure.annotations.Description;
import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Step;
import ru.yandex.qatools.allure.annotations.Stories;
import static org.fest.assertions.api.Assertions.assertThat;
/**
* Test class for {@link AutoAssignChecker}.
*
*/
@Features("Component Tests - Repository")
@Stories("Auto assign checker")
public class AutoAssignCheckerTest extends AbstractJpaIntegrationTest {
@Autowired
private AutoAssignChecker autoAssignChecker;
@Test
@Description("Test auto assignment of a DS to filtered targets")
public void checkAutoAssign() {
final DistributionSet setA = testdataFactory.createDistributionSet("dsA"); // will be auto assigned
final DistributionSet setB = testdataFactory.createDistributionSet("dsB");
// target filter query that matches all targets
TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
.createTargetFilterQuery(new JpaTargetFilterQuery("filterA", "name==*"));
targetFilterQuery.setAutoAssignDistributionSet(setA);
targetFilterQueryManagement.updateTargetFilterQuery(targetFilterQuery);
final String targetDsAIdPref = "targ";
List<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.countTargetByTargetFilterQueryAndNonDS(setA.getId(), targetFilterQuery))
.isEqualTo(90);
// Run the check
autoAssignChecker.check();
verifyThatTargetsHaveDistributionSetAssignment(setA, targets.subList(5, 100), targetsCount);
// first 5 should keep their dsB, because they already had the dsA once
verifyThatTargetsHaveDistributionSetAssignment(setB, targets.subList(0, 5), targetsCount);
}
@Test
@Description("Test auto assignment of an incomplete DS to filtered targets, that causes failures")
public void checkAutoAssignWithFailures() {
// incomplete distribution set that will be assigned
final DistributionSet setF = distributionSetManagement
.createDistributionSet(entityFactory.generateDistributionSet("dsA", "1", "incomplete ds",
testdataFactory.findOrCreateDefaultTestDsType(), null));
final DistributionSet setA = testdataFactory.createDistributionSet("dsA");
final DistributionSet setB = testdataFactory.createDistributionSet("dsB");
final String targetDsAIdPref = "targA";
final String targetDsFIdPref = "targB";
// target filter query that matches first bunch of targets, that should
// fail
targetFilterQueryManagement.createTargetFilterQuery(
new JpaTargetFilterQuery("filterA", "id==" + targetDsFIdPref + "*", (JpaDistributionSet) setF));
// target filter query that matches failed bunch of targets
targetFilterQueryManagement.createTargetFilterQuery(
new JpaTargetFilterQuery("filterB", "id==" + targetDsAIdPref + "*", (JpaDistributionSet) setA));
List<Target> targetsF = targetManagement.createTargets(
testdataFactory.generateTargets(10, targetDsFIdPref, targetDsFIdPref.concat(" description")));
List<Target> targetsA = targetManagement.createTargets(
testdataFactory.generateTargets(10, targetDsAIdPref, targetDsAIdPref.concat(" description")));
int targetsCount = targetsA.size() + targetsF.size();
// assign set B to first 5 targets of fail group
deploymentManagement.assignDistributionSet(setB, targetsF.subList(0, 5));
verifyThatTargetsHaveDistributionSetAssignment(setB, targetsF.subList(0, 5), targetsCount);
// Run the check
autoAssignChecker.check();
// first 5 targets of the fail group should still have setB
verifyThatTargetsHaveDistributionSetAssignment(setB, targetsF.subList(0, 5), targetsCount);
// all targets of A group should have received setA
verifyThatTargetsHaveDistributionSetAssignment(setA, targetsA, targetsCount);
}
/**
* @param set the expected distribution set
* @param targets the targets that should have it
*/
@Step
private void verifyThatTargetsHaveDistributionSetAssignment(final DistributionSet set, List<Target> targets, int count) {
List<Long> targetIds = targets.stream().map(Target::getId).collect(Collectors.toList());
Slice<Target> targetsAll = targetManagement.findTargetsAll(new PageRequest(0, 1000));
assertThat(targetsAll).as("Count of targets").hasSize(count);
for (Target target : targetsAll) {
if(targetIds.contains(target.getId())) {
assertThat(target.getAssignedDistributionSet()).as("assigned DS").isEqualTo(set);
}
}
}
}

View File

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

View File

@@ -0,0 +1,107 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ui.common.detailslayout;
import java.util.List;
import com.vaadin.data.Container;
import com.vaadin.data.Item;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import com.vaadin.data.util.IndexedContainer;
import com.vaadin.spring.annotation.SpringComponent;
import com.vaadin.spring.annotation.VaadinSessionScope;
import com.vaadin.ui.Table;
import com.vaadin.ui.themes.ValoTheme;
import org.eclipse.hawkbit.ui.utils.I18N;
import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions;
/**
*
* DistributionSet TargetFilterQuery table
*
*/
@SpringComponent
@VaadinSessionScope
public class TargetFilterQueryDetailsTable extends Table {
private static final long serialVersionUID = 2913758299611837718L;
private static final String TFQ_NAME = "name";
private static final String TFQ_QUERY = "query";
private I18N i18n;
/**
*
* @param i18n
*/
public void init(final I18N i18n) {
this.i18n = i18n;
createTable();
}
/**
* Populate software module metadata.
*
* @param distributionSet the selected distribution set
*/
public void populateTableByDistributionSet(final DistributionSet distributionSet) {
removeAllItems();
if (null == distributionSet) {
return;
}
Container dataSource = getContainerDataSource();
List<TargetFilterQuery> filters = distributionSet.getAutoAssignFilters();
filters.forEach(query -> {
Object itemId = dataSource.addItem();
Item item = dataSource.getItem(itemId);
item.getItemProperty(TFQ_NAME).setValue(query.getName());
item.getItemProperty(TFQ_QUERY).setValue(query.getQuery());
});
}
private void createTable() {
addStyleName(ValoTheme.TABLE_NO_HORIZONTAL_LINES);
addStyleName(ValoTheme.TABLE_NO_STRIPES);
addStyleName(SPUIStyleDefinitions.SW_MODULE_TABLE);
addStyleName("details-layout");
setSelectable(false);
setImmediate(true);
setContainerDataSource(getDistSetContainer());
setColumnHeaderMode(ColumnHeaderMode.EXPLICIT);
addTableHeader();
setSizeFull();
// same as height of other tabs in details tabsheet
setHeight(116, Unit.PIXELS);
}
private IndexedContainer getDistSetContainer() {
final IndexedContainer container = new IndexedContainer();
container.addContainerProperty(TFQ_NAME, String.class, "");
container.addContainerProperty(TFQ_QUERY, String.class, "");
setColumnExpandRatio(TFQ_NAME, 0.4F);
setColumnAlignment(TFQ_NAME, Align.LEFT);
setColumnExpandRatio(TFQ_QUERY, 0.6F);
setColumnAlignment(TFQ_QUERY, Align.LEFT);
return container;
}
private void addTableHeader() {
setColumnHeader(TFQ_NAME, i18n.get("header.target.filter.name"));
setColumnHeader(TFQ_QUERY, i18n.get("header.target.filter.query"));
}
}

View File

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

View File

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

View File

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

View File

@@ -20,10 +20,8 @@ import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetFilter;
import org.eclipse.hawkbit.repository.model.DistributionSetFilter.DistributionSetFilterBuilder;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.ui.common.UserDetailsFormatter;
import org.eclipse.hawkbit.ui.components.ProxyDistribution;
import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil;
import org.eclipse.hawkbit.ui.utils.SPDateTimeUtil;
import org.eclipse.hawkbit.ui.utils.SPUIDefinitions;
import org.eclipse.hawkbit.ui.utils.SpringContextHelper;
import org.springframework.data.domain.Page;
@@ -48,6 +46,7 @@ public class ManageDistBeanQuery extends AbstractBeanQuery<ProxyDistribution> {
private transient Page<DistributionSet> firstPageDistributionSets = null;
private DistributionSetType distributionSetType = null;
private Boolean dsComplete = null;
/**
*
@@ -69,6 +68,9 @@ public class ManageDistBeanQuery extends AbstractBeanQuery<ProxyDistribution> {
distributionSetType = (DistributionSetType) queryConfig
.get(SPUIDefinitions.FILTER_BY_DISTRIBUTION_SET_TYPE);
}
if(null != queryConfig.get(SPUIDefinitions.FILTER_BY_DS_COMPLETE)) {
dsComplete = (Boolean)queryConfig.get(SPUIDefinitions.FILTER_BY_DS_COMPLETE);
}
}
if (sortStates.length > 0) {
@@ -96,27 +98,17 @@ public class ManageDistBeanQuery extends AbstractBeanQuery<ProxyDistribution> {
} else if (Strings.isNullOrEmpty(searchText)) {
// if no search filters available
distBeans = getDistributionSetManagement().findDistributionSetsByDeletedAndOrCompleted(
new OffsetBasedPageRequest(startIndex, count, sort), false, null);
new OffsetBasedPageRequest(startIndex, count, sort), false, dsComplete);
} else {
final DistributionSetFilter distributionSetFilter = new DistributionSetFilterBuilder().setIsDeleted(false)
.setIsComplete(dsComplete)
.setSearchText(searchText).setSelectDSWithNoTag(Boolean.FALSE).setType(distributionSetType).build();
distBeans = getDistributionSetManagement().findDistributionSetsByFilters(
new PageRequest(startIndex / count, count, sort), distributionSetFilter);
}
for (final DistributionSet distributionSet : distBeans) {
final ProxyDistribution proxyDistribution = new ProxyDistribution();
proxyDistribution.setName(distributionSet.getName());
proxyDistribution.setDescription(distributionSet.getDescription());
proxyDistribution.setDistId(distributionSet.getId());
proxyDistribution.setId(distributionSet.getId());
proxyDistribution.setVersion(distributionSet.getVersion());
proxyDistribution.setCreatedDate(SPDateTimeUtil.getFormattedDate(distributionSet.getCreatedAt()));
proxyDistribution.setLastModifiedDate(SPDateTimeUtil.getFormattedDate(distributionSet.getLastModifiedAt()));
proxyDistribution.setCreatedByUser(UserDetailsFormatter.loadAndFormatCreatedBy(distributionSet));
proxyDistribution.setModifiedByUser(UserDetailsFormatter.loadAndFormatLastModifiedBy(distributionSet));
proxyDistribution.setIsComplete(distributionSet.isComplete());
proxyDistributions.add(proxyDistribution);
proxyDistributions.add(new ProxyDistribution(distributionSet));
}
return proxyDistributions;
}
@@ -132,9 +124,10 @@ public class ManageDistBeanQuery extends AbstractBeanQuery<ProxyDistribution> {
if (Strings.isNullOrEmpty(searchText) && null == distributionSetType) {
// if no search filters available
firstPageDistributionSets = getDistributionSetManagement().findDistributionSetsByDeletedAndOrCompleted(
new PageRequest(0, SPUIDefinitions.PAGE_SIZE, sort), false, null);
new PageRequest(0, SPUIDefinitions.PAGE_SIZE, sort), false, dsComplete);
} else {
final DistributionSetFilter distributionSetFilter = new DistributionSetFilterBuilder().setIsDeleted(false)
.setIsComplete(dsComplete)
.setSearchText(searchText).setSelectDSWithNoTag(Boolean.FALSE).setType(distributionSetType).build();
firstPageDistributionSets = getDistributionSetManagement().findDistributionSetsByFilters(
new PageRequest(0, SPUIDefinitions.PAGE_SIZE, sort), distributionSetFilter);

View File

@@ -0,0 +1,182 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ui.filtermanagement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.eclipse.hawkbit.repository.eventbus.event.DistributionCreatedEvent;
import org.eclipse.hawkbit.repository.eventbus.event.DistributionDeletedEvent;
import org.eclipse.hawkbit.ui.distributions.dstable.ManageDistBeanQuery;
import org.eclipse.hawkbit.ui.distributions.state.ManageDistUIState;
import org.eclipse.hawkbit.ui.utils.I18N;
import org.eclipse.hawkbit.ui.utils.SPUIDefinitions;
import org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions;
import org.eclipse.hawkbit.ui.utils.TableColumn;
import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.vaadin.addons.lazyquerycontainer.BeanQueryFactory;
import org.vaadin.addons.lazyquerycontainer.LazyQueryContainer;
import org.vaadin.addons.lazyquerycontainer.LazyQueryDefinition;
import org.vaadin.spring.events.EventBus;
import org.vaadin.spring.events.EventScope;
import org.vaadin.spring.events.annotation.EventBusListenerMethod;
import com.vaadin.data.Container;
import com.vaadin.spring.annotation.SpringComponent;
import com.vaadin.spring.annotation.ViewScope;
import com.vaadin.ui.Table;
import com.vaadin.ui.themes.ValoTheme;
/**
* Table for selecting a distribution set.
*/
@SpringComponent
@ViewScope
public class DistributionSetSelectTable extends Table {
private static final long serialVersionUID = -4307487829435471759L;
@Autowired
private I18N i18n;
@Autowired
private transient EventBus.SessionEventBus eventBus;
@Autowired
private ManageDistUIState manageDistUIState;
private Container container;
/**
* Initialize the component.
*/
@PostConstruct
protected void init() {
setStyleName("sp-table");
setSizeFull();
setSelectable(true);
setMultiSelect(false);
setImmediate(true);
addStyleName(ValoTheme.TABLE_NO_VERTICAL_LINES);
addStyleName(ValoTheme.TABLE_SMALL);
populateTableData();
setColumnCollapsingAllowed(false);
setColumnProperties();
setId(UIComponentIdProvider.DIST_SET_SELECT_TABLE_ID);
eventBus.subscribe(this);
}
@PreDestroy
void destroy() {
eventBus.unsubscribe(this);
}
@EventBusListenerMethod(scope = EventScope.SESSION)
void onEvents(final List<?> events) {
final Object firstEvent = events.get(0);
if (DistributionCreatedEvent.class.isInstance(firstEvent)
|| DistributionDeletedEvent.class.isInstance(firstEvent)) {
refreshDistributions();
}
}
private void populateTableData() {
container = createContainer();
addContainerproperties();
setContainerDataSource(container);
setColumnProperties();
}
protected Container createContainer() {
final Map<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<>();
columnList.add(new TableColumn(SPUILabelDefinitions.NAME, i18n.get("header.name"), 0.6F));
columnList.add(new TableColumn(SPUILabelDefinitions.VAR_VERSION, i18n.get("header.version"), 0.4F));
return columnList;
}
private void setColumnProperties() {
final List<TableColumn> columnList = getVisbleColumns();
final List<Object> swColumnIds = new ArrayList<>();
for (final TableColumn column : columnList) {
setColumnHeader(column.getColumnPropertyId(), column.getColumnHeader());
setColumnExpandRatio(column.getColumnPropertyId(), column.getExpandRatio());
swColumnIds.add(column.getColumnPropertyId());
}
setVisibleColumns(swColumnIds.toArray());
}
private Map<String, Object> prepareQueryConfigFilters() {
final Map<String, Object> queryConfig = new HashMap<>();
manageDistUIState.getManageDistFilters().getSearchText()
.ifPresent(value -> queryConfig.put(SPUIDefinitions.FILTER_BY_TEXT, value));
if (null != manageDistUIState.getManageDistFilters().getClickedDistSetType()) {
queryConfig.put(SPUIDefinitions.FILTER_BY_DISTRIBUTION_SET_TYPE,
manageDistUIState.getManageDistFilters().getClickedDistSetType());
}
queryConfig.put(SPUIDefinitions.FILTER_BY_DS_COMPLETE, Boolean.TRUE);
return queryConfig;
}
private void refreshDistributions() {
final LazyQueryContainer dsContainer = (LazyQueryContainer) getContainerDataSource();
final int size = dsContainer.size();
if (size < SPUIDefinitions.MAX_TABLE_ENTRIES) {
refreshTablecontainer();
}
if (size != 0) {
setData(SPUIDefinitions.DATA_AVAILABLE);
}
}
private Object getItemIdToSelect() {
if (manageDistUIState.getSelectedDistributions().isPresent()) {
return manageDistUIState.getSelectedDistributions().get();
}
return null;
}
private void selectRow() {
setValue(getItemIdToSelect());
}
private void refreshTablecontainer() {
final LazyQueryContainer dsContainer = (LazyQueryContainer) getContainerDataSource();
dsContainer.refresh();
selectRow();
}
}

View File

@@ -0,0 +1,307 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ui.filtermanagement;
import com.vaadin.server.FontAwesome;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Button;
import com.vaadin.ui.CheckBox;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.ui.common.CommonDialogWindow;
import org.eclipse.hawkbit.ui.common.DistributionSetIdName;
import org.eclipse.hawkbit.ui.common.builder.WindowBuilder;
import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleNoBorderWithIcon;
import org.eclipse.hawkbit.ui.filtermanagement.event.CustomFilterUIEvent;
import org.eclipse.hawkbit.ui.utils.I18N;
import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider;
import org.eclipse.hawkbit.ui.utils.SPUIDefinitions;
import org.springframework.beans.factory.annotation.Autowired;
import org.vaadin.spring.events.EventBus;
import com.vaadin.data.Property;
import com.vaadin.server.Sizeable;
import com.vaadin.spring.annotation.SpringComponent;
import com.vaadin.spring.annotation.ViewScope;
import java.io.Serializable;
/**
* Creates a dialog window to select the distribution set for a target filter query.
*/
@SpringComponent
@ViewScope
public class DistributionSetSelectWindow
implements CommonDialogWindow.SaveDialogCloseListener, Property.ValueChangeListener {
private static final long serialVersionUID = 4752345414134989396L;
@Autowired
private I18N i18n;
@Autowired
private DistributionSetSelectTable dsTable;
@Autowired
private transient EventBus.SessionEventBus eventBus;
@Autowired
private transient DistributionSetManagement distributionSetManagement;
@Autowired
private transient TargetManagement targetManagement;
@Autowired
private transient TargetFilterQueryManagement targetFilterQueryManagement;
private CommonDialogWindow window;
private CheckBox checkBox;
private VerticalLayout verticalLayout;
private Long tfqId;
private void init() {
Label label = new Label(i18n.get("label.auto.assign.description"));
checkBox = new CheckBox(i18n.get("label.auto.assign.enable"));
checkBox.setId(UIComponentIdProvider.DIST_SET_SELECT_ENABLE_ID);
checkBox.setImmediate(true);
checkBox.addValueChangeListener(this);
verticalLayout = new VerticalLayout();
verticalLayout.addComponent(label);
verticalLayout.addComponent(checkBox);
verticalLayout.addComponent(dsTable);
window = new WindowBuilder(SPUIDefinitions.CREATE_UPDATE_WINDOW)
.caption(i18n.get("caption.select.auto.assign.dist")).content(verticalLayout).layout(verticalLayout)
.i18n(i18n).saveDialogCloseListener(this).buildCommonDialogWindow();
window.setId(UIComponentIdProvider.DIST_SET_SELECT_WINDOW_ID);
}
public void setValue(DistributionSetIdName distSet) {
dsTable.setVisible(distSet != null);
checkBox.setValue(distSet != null);
dsTable.setValue(distSet);
dsTable.setCurrentPageFirstItemId(distSet);
}
public DistributionSetIdName getValue() {
if (checkBox.getValue()) {
return (DistributionSetIdName) dsTable.getValue();
}
return null;
}
/**
* Shows a distribution set select window for the given target filter query
*
* @param tfqId
* target filter query id
*/
public void showForTargetFilter(Long tfqId) {
this.tfqId = tfqId;
TargetFilterQuery tfq = targetFilterQueryManagement.findTargetFilterQueryById(tfqId);
if(tfq == null) {
throw new IllegalStateException("TargetFilterQuery does not exist for the given id");
}
init();
DistributionSet distributionSet = tfq.getAutoAssignDistributionSet();
if(distributionSet != null) {
setValue(DistributionSetIdName.generate(distributionSet));
} else {
setValue(null);
}
window.setWidth(40.0F, Sizeable.Unit.PERCENTAGE);
UI.getCurrent().addWindow(window);
window.setVisible(true);
}
/**
* Is triggered when the checkbox value changes
*
* @param event
* change event
*/
@Override
public void valueChange(Property.ValueChangeEvent event) {
dsTable.setVisible(checkBox.getValue());
if (window != null) {
window.center();
}
}
/**
* Is triggered when the save button is clicked
*
* @return whether the click should be allowed
*/
@Override
public boolean canWindowSaveOrUpdate() {
return !checkBox.getValue() || dsTable.getValue() != null;
}
/**
* Is called when the new value should be saved after the save button has
* been clicked
*/
@Override
public void saveOrUpdate() {
if(checkBox.getValue() && dsTable.getValue() != null) {
DistributionSetIdName ds = (DistributionSetIdName) dsTable.getValue();
updateTargetFilterQueryDS(tfqId, ds.getId());
} else if(!checkBox.getValue()) {
updateTargetFilterQueryDS(tfqId, null);
}
}
private void updateTargetFilterQueryDS(final Long targetFilterQueryId, final Long dsId) {
TargetFilterQuery tfq = targetFilterQueryManagement.findTargetFilterQueryById(targetFilterQueryId);
if(dsId != null) {
confirmWithConsequencesDialog(tfq, dsId);
} else {
tfq.setAutoAssignDistributionSet(null);
targetFilterQueryManagement.updateTargetFilterQuery(tfq);
eventBus.publish(this, CustomFilterUIEvent.UPDATED_TARGET_FILTER_QUERY);
}
}
private void confirmWithConsequencesDialog(TargetFilterQuery tfq, final Long dsId) {
ConfirmConsequencesDialog dialog = new ConfirmConsequencesDialog(tfq, dsId, new ConfirmCallback() {
@Override
public void onConfirmResult(boolean accepted) {
if(accepted) {
tfq.setAutoAssignDistributionSet(distributionSetManagement.findDistributionSetById(dsId));
targetFilterQueryManagement.updateTargetFilterQuery(tfq);
eventBus.publish(this, CustomFilterUIEvent.UPDATED_TARGET_FILTER_QUERY);
}
}
});
dialog.setWidth(40.0F, Sizeable.Unit.PERCENTAGE);
UI.getCurrent().addWindow(dialog);
dialog.setVisible(true);
}
/**
* A dialog that displays how many targets will be assigned immediately with the
*/
private class ConfirmConsequencesDialog extends Window implements Button.ClickListener {
private static final long serialVersionUID = 7738545414137389326L;
private TargetFilterQuery targetFilterQuery;
private Long distributionSetId;
private Button okButton;
private Button cancelButton;
private ConfirmCallback callback;
public ConfirmConsequencesDialog(TargetFilterQuery targetFilterQuery, final Long dsId, ConfirmCallback callback) {
super(i18n.get("caption.confirm.assign.consequences"));
this.callback = callback;
this.targetFilterQuery = targetFilterQuery;
this.distributionSetId = dsId;
init();
}
private void init() {
setId(UIComponentIdProvider.DIST_SET_SELECT_CONS_WINDOW_ID);
setModal(true);
setResizable(false);
VerticalLayout layout = new VerticalLayout();
layout.setSpacing(true);
layout.setMargin(true);
setContent(layout);
Long targetsCount = targetManagement.countTargetByTargetFilterQueryAndNonDS(distributionSetId, targetFilterQuery);
Label mainTextLabel;
if(targetsCount == 0) {
mainTextLabel = new Label(i18n.get("message.confirm.assign.consequences.none"));
} else {
mainTextLabel = new Label(i18n.get("message.confirm.assign.consequences.text", new Object[]{targetsCount}));
}
layout.addComponent(mainTextLabel);
HorizontalLayout buttonsLayout = new HorizontalLayout();
buttonsLayout.setSizeFull();
buttonsLayout.setSpacing(true);
buttonsLayout.addStyleName("actionButtonsMargin");
layout.addComponent(buttonsLayout);
okButton = SPUIComponentProvider.getButton(UIComponentIdProvider.SAVE_BUTTON, i18n.get("button.ok"), "", "", true,
FontAwesome.SAVE, SPUIButtonStyleNoBorderWithIcon.class);
okButton.setSizeUndefined();
okButton.addStyleName("default-color");
okButton.addClickListener(this);
buttonsLayout.addComponent(okButton);
buttonsLayout.setComponentAlignment(okButton, Alignment.MIDDLE_RIGHT);
buttonsLayout.setExpandRatio(okButton, 1.0F);
cancelButton = SPUIComponentProvider.getButton(UIComponentIdProvider.CANCEL_BUTTON, i18n.get("button.cancel"), "", "", true,
FontAwesome.TIMES, SPUIButtonStyleNoBorderWithIcon.class);
cancelButton.setSizeUndefined();
cancelButton.addStyleName("default-color");
cancelButton.addClickListener(this);
buttonsLayout.addComponent(cancelButton);
buttonsLayout.setComponentAlignment(cancelButton, Alignment.MIDDLE_LEFT);
buttonsLayout.setExpandRatio(cancelButton, 1.0F);
}
@Override
public void buttonClick(Button.ClickEvent event) {
if(event.getButton().getId().equals(okButton.getId())) {
callback.onConfirmResult(true);
} else {
callback.onConfirmResult(false);
}
close();
}
}
@FunctionalInterface
private interface ConfirmCallback extends Serializable {
void onConfirmResult(boolean accepted);
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -877,6 +877,26 @@ public final class UIComponentIdProvider {
*/
public static final String DOWNLOAD_ANONYMOUS_CHECKBOX = "downloadanonymouscheckbox";
/**
* Distribution set select table id
*/
public static final String DIST_SET_SELECT_TABLE_ID = "distribution.set.select.table";
/**
* Distribution set select window id
*/
public static final String DIST_SET_SELECT_WINDOW_ID = "distribution.set.select.window";
/**
* Distribution set select consequences window id
*/
public static final String DIST_SET_SELECT_CONS_WINDOW_ID = "distribution.set.select.consequences.window";
/**
* Distribution set select enable checkbox id
*/
public static final String DIST_SET_SELECT_ENABLE_ID = "distribution.set.select.enable";
/**
* /* Private Constructor.
*/

View File

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

View File

@@ -22,6 +22,8 @@ button.no.actions = No actions
button.ok = OK
button.cancel = Cancel
button.upload.file = Upload File
button.no.auto.assignment = none
button.auto.assignment.desc = Select auto assign distribution set
bulk.targets.upload = Please upload csv file.
bulkupload.ds.name = DS Name
@@ -59,6 +61,7 @@ caption.filter.simple = Simple Filter
caption.filter.custom = Custom Filter
caption.metadata = Metadata
caption.select.auto.assign.dist = Select auto assignment distribution set
caption.add.softwaremodule = Configure Software Module
caption.add.new.dist = Configure New Distribution
caption.update.dist = Configure Update Distribution
@@ -92,26 +95,27 @@ caption.confirm.abort.action = Confirm Abort Action
caption.filter.delete.confirmbox = Confirm Filter Delete Action
caption.metadata.popup = Metadata of
caption.metadata.delete.action.confirmbox = Confirm Metadata Delete Action
caption.confirm.assign.consequences = Auto assign consequences
caption.auto.assignment.ds = Auto assignment
# Labels prefix with - label
label.dist.details.type = Type :
label.dist.details.name = Name :
label.dist.details.version = Version :
label.dist.details.vendor = Vendor :
label.dist.details.jvm = Runtime :
label.dist.details.ah = Application :
label.dist.details.os = OS :
label.modified.date = Last modified at :
label.modified.by = Last modified by :
label.created.at = Created at :
label.created.by = Created by :
label.target.count = Targets :
label.description = Description :
label.ip = Address :
label.type = Type :
label.assigned.type = Assignment type :
label.assigned.count = {0} Assigned
label.dist.details.type = Type :
label.dist.details.name = Name :
label.dist.details.version = Version :
label.dist.details.vendor = Vendor :
label.dist.details.jvm = Runtime :
label.dist.details.ah = Application :
label.dist.details.os = OS :
label.modified.date = Last modified at :
label.modified.by = Last modified by :
label.created.at = Created at :
label.created.by = Created by :
label.target.count = Targets :
label.description = Description :
label.ip = Address :
label.type = Type :
label.assigned.type = Assignment type :
label.assigned.count = {0} Assigned
label.installed.count = {0} Installed
label.mandatory.field = * Mandatory Field
label.components.drop.area = Drop here to delete
@@ -133,10 +137,6 @@ label.target.filter.count = Total Targets:
label.target.filtered.total = Total filtered targets :
label.filter.selected = Selected:
label.filter.shown = Shown:
label.filter = Filter :
label.target.filter.count = Total Targets :
label.filter.selected = Selected :
label.filter.shown = Shown :
label.filter.targets = Filtered targets :
label.filter.status = Status,
label.filter.tags = Tags,
@@ -155,9 +155,9 @@ label.cancelling = Canceling
label.retrieved = Retrieved
label.download = Downloading
label.unknown = Unknown
label.target.id = Controller Id :
label.target.ip = Controller IP :
label.target.security.token = Security token :
label.target.id = Controller Id :
label.target.ip = Controller IP :
label.target.security.token = Security token :
label.filter.by.status = Filter by Status
label.target.controller.attrs = <b>Controller attributes</b>
label.target.lastpolldate = Last poll :
@@ -168,9 +168,11 @@ label.configuration.auth.gatewaytoken = Allow a gateway to authenticate and mana
label.configuration.auth.targettoken = Allow targets to authenticate directly with their target security token
label.configuration.anonymous.download = Allow targets to download artifacts without security credentials
label.unsupported.browser.ie=Sorry! current browser is not supported. Please use Internet Explorer 11 and above
label.auto.assign.description=When an auto assign distribution set is selected, it will be automatically assigned to all targets that match the target filter.
label.auto.assign.enable=Enable auto assignment
# Checkbox label prefix with - checkbox
checkbox.dist.migration.required = Required Migration Step :
checkbox.dist.migration.required = Required Migration Step :
checkbox.dist.required.migration.step = Required Migration Step
# TextFields prefix with - textfield
@@ -186,7 +188,7 @@ prompt.target.id = Controller ID
#Tooltips prefix with - tooltip
tooltip.add.module = Add Software Module
tooltip.add.module = Add Software Module
tooltip.status.unknown = Unknown
tooltip.status.registered = Registered
tooltip.status.pending = Pending
@@ -240,7 +242,7 @@ message.software.type.discard.success = All software moduleTypes selected for de
message.assign.software.discard.success = All software moduleTypes selected for assign are discarded successfully !
message.software.delete.success = All software modules selected for delete are deleted successfully !
message.software.type.delete.success = All software modules types selected for delete are deleted successfully !
message.dist.set.type.deleted.success = {0} DistributionSetType deleted successfully !
message.dist.set.type.deleted.success = {0} DistributionSetType deleted successfully !
message.new.dist.save.success = {0} - {1} saved successfully
message.dist.update.success = {0} - {1} updated successfully
message.duplicate.dist = Distribution set [{0}] or version [{1}] must be unique, entered value already exists.
@@ -248,7 +250,7 @@ message.error.view = No such view: {0}
message.accessdenied.view = No access to view: {0}
message.no.data = No data available
message.target.assignment = {0} Assignment(s) done
message.target.deleted = {0} Target(s) deleted
message.target.deleted = {0} Target(s) deleted
message.dist.deleted = {0} Distribution set(s) deleted
message.tag.update.mandatory = Please select the Tag to update
message.tag.duplicate.check = {0} already exists, please enter another value
@@ -274,7 +276,7 @@ message.error.ah.softmodule = Please select the Application to delete
message.error.softmodule.deleted = The selected Software Module is already deleted
message.cancel.action = Cancel..
message.cancel.action.success = Action cancelled successfully !
message.cancel.action.failed = Unable to cancel the action !
message.cancel.action.failed = Unable to cancel the action !
message.cancel.action.confirm = Are you sure that you want to cancel this action?
message.target.alreadyAssigned = {0} Target(s) were already assigned
message.dist.alreadyAssigned = {0} Distribution Set(s) were already assigned
@@ -283,8 +285,8 @@ message.force.action.confirm = Are you sure that you want to force this action?
message.force.action.success = Action forced successfully !
message.forcequit.action = Force Quit..
message.forcequit.action.success = Action has been force quit successfully !
message.forcequit.action.failed = Force Quitting the action is not possible !
message.forcequit.action.confirm = Attention!\nForce quit should only be used when the assignment action is not working properly.\nForce quitting an action has no effect on the connected target. It is just resetting \nthe data stored on the SP update server. \nAre you absolutely sure that you want to force quit this action?
message.forcequit.action.failed = Force Quitting the action is not possible !
message.forcequit.action.confirm = Attention!\nForce quit should only be used when the assignment action is not working properly.\nForce quitting an action has no effect on the connected target. It is just resetting \nthe data stored on the SP update server. \nAre you absolutely sure that you want to force quit this action?
message.distribution.no.update = distribution {0} set is already assigned to targets and cannot be changed
message.action.not.allowed = Action not allowed
message.action.did.not.work = Action did not work. Please try again.
@@ -306,12 +308,14 @@ message.update.filter.success = Custom filter updated Successfully!
message.target.filter.validation = Please enter name and query
message.target.filter.duplicate = {0} already exists, please enter another value
message.tag.use.bulk.upload = {0} cannot be deleted .It is in use in targets bulk upload
message.bulk.upload.tag.assignment.failed = Tag {0} assignment failed as tag no longer exists
message.bulk.upload.tag.assignments.failed= Few tag assignments failed as tags no longer exists
message.bulk.upload.tag.assignment.failed = Tag {0} assignment failed as tag no longer exists
message.bulk.upload.tag.assignments.failed= Few tag assignments failed as tags no longer exists
message.confirm.assign.consequences.none = This auto assignment will not have any effect on the currently available targets. In future added targets might match the filter and will receive the selected distribution set automatically.
message.confirm.assign.consequences.text = When you confirm this auto assignment, {0} targets which match the filter will immediately get assigned with the selected distribution set.
# action info
action.target.table.selectall = Select all (Ctrl+A)
action.target.table.clear = Clear selections
action.target.table.clear = Clear selections
#reused messages
soft.module.jvm =Runtime
@@ -322,8 +326,8 @@ soft.module.os =OS
message.error.noFileSelected = No file selected for upload
message.error.noProvidedName = Please provide custom file name
message.error.noSwModuleSelected = Please select a Software Module
message.no.duplicateFiles = Duplicate files selected
message.no.duplicateFile = Duplicate file selected :
message.no.duplicateFiles = Duplicate files selected
message.no.duplicateFile = Duplicate file selected :
message.delete.artifact = Are you sure that you want to delete artifact {0} ?
message.duplicate.filename = Duplicate file name
message.swModule.deleted = {0} Software Module(s) deleted
@@ -337,7 +341,7 @@ message.abort.upload = Are you sure that you want to abort the upload?
upload.swModuleTable.header = Software Module
upload.selectedfile.name = file selected for upload
upload.selectedfile.name = file selected for upload
upload.file.name = File name
upload.sha1 = SHA1 checksum
upload.md5 = MD5 checksum
@@ -366,7 +370,6 @@ custom.created.date = Created Date
#Manage distributions view
label.drop.dist.delete.area = Drop here<br>to delete
label.no.tag.assigned = NO TAG
caption.assign.software.dist.accordion.tab = Assign Software Modules
message.software.assignment = {0} Software Module(s) Assignment(s) done
message.dist.inuse = {0} Distribution is already assigned to target
@@ -376,9 +379,7 @@ message.target.assigned = {0} is assigned to {1}
message.dist.type.delete = {0} DistributionType(s) Deleted successfully.
message.sw.module.type.delete = {0} Software Module Type(s) deleted successfully.
message.dist.type.discard.success = All Distribution Types are discarded successfully !
message.dist.discard.success = All Distributions are discarded successfully !
message.assign.discard.success = All assignments are discarded successfully !
message.target.ds.assign.success = Assignments saved successfully !
message.bulk.upload.assignment.failed = Distribution set assignment failed as distribution set no longer exists!
message.key.missing = Key is missing !
message.value.missing = Value is missing !
@@ -449,12 +450,12 @@ header.assigned.ds = Assigned DS
header.installed.ds = Installed DS
header.target.status = Status
header.target.tags = Tags
header.distributionset = Distribution set
header.numberofgroups = No. of groups
header.detail.status = Detail status
header.total.targets = Total targets
header.total.targets = Total targets
header.key = Key
header.value = Value
header.auto.assignment.ds = Auto assignment
header.target.filter.name = Target filter name
header.target.filter.query = Target filter query
distribution.details.header = Distribution set
target.details.header = Target

View File

@@ -1,505 +0,0 @@
#
# Copyright (c) 2015 Bosch Software Innovations GmbH and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v1.0
# which accompanies this distribution, and is available at
# http://www.eclipse.org/legal/epl-v10.html
#
#########################################################################################
# This is the messages_en.properties file
#########################################################################################
# Button names prefix with - button
button.save = Save
button.delete = Delete
button.discard = Discard
button.discard.all = Discard All
button.delete.all = Delete All
button.assign.all = Save Assign
button.actions = You have actions
button.no.actions = No actions
button.ok = OK
button.cancel = Cancel
button.upload.file = Upload File
bulk.targets.upload = Please upload csv file.
bulkupload.ds.name = DS Name
# Headers prefix with - header
header.target.table=Targets
header.dist.table=Distributions
header.filter.tag=Filter by Tag
header.target.filter.tag=Filters
header.first.assignment.table = Targets
header.second.assignment.table = Distributions
header.dist.first.assignment.table = Distributions
header.dist.second.assignment.table = Software Modules
header.third.assignment.table = Discard
header.one.deletedist.table = Distribution Name
header.second.deletedist.table = Discard Changes
header.first.deletetarget.table = Target Name
header.second.deletetarget.table = Discard Changes
header.first.deleteswmodule.table = Delete software
header.first.delete.dist.type.table = DistributionSetType
header.second.delete.dist.type.table = Discard
header.first.delete.swmodule.type.table = Software Module Type
header.second.delete.swmodule.type.table = Discard
header.dist.twintable.selected=Selected
header.dist.twintable.available=Available
header.target.installed = Installed
header.target.assigned = Assigned
# Captions prefix with - caption
caption.action.history = Action history for {0}
caption.error = Error
caption.new.softwaremodule.application = Configure New Application
caption.new.softwaremodule.jvm = Configure New Runtime
caption.new.softwaremodule.os = Configure New OS
caption.metadata = Metadata
caption.add.softwaremodule = Configure Software Module
caption.add.new.dist = Configure New Distribution
caption.update.dist = Configure Update Distribution
caption.add.tag = Configure Tag
caption.add.type = Configure Type
caption.add.new.target = Configure New Target
caption.update.target = Configure Update Target
caption.bulk.upload.targets = Bulk Upload
caption.softwares.distdetail.tab = Modules
caption.tags.tab = Tags
caption.logs.tab = Logs
caption.attributes.tab = Attributes
caption.types.tab = Types
caption.save.window = Action Details
caption.assign.dist.accordion.tab = Assign Distribution Set
caption.delete.dist.accordion.tab = Delete Distributions
caption.delete.target.accordion.tab = Delete Targets
caption.delete.swmodule.accordion.tab = Delete SW Modules
caption.delete.dist.set.type.accordion.tab = Delete Distribution Set Type
caption.delete.sw.module.type.accordion.tab = Delete Software Module Type
caption.attributes = Attributes
caption.panel.dist.installed = Installed Distribution Set
caption.panel.dist.assigned = Assigned Distribution Set
caption.soft.delete.confirmbox = Confirm Software Module Delete Action
caption.cancel.action.confirmbox = Confirm Action Cancellation
caption.forced.datefield = Force update at time
caption.force.action.confirmbox = Confirm Force Active Action
caption.filter.simple = Simple Filter
caption.filter.custom = Custom Filter
caption.filter.delete.confirmbox = Confirm Filter Delete Action
caption.confirm.abort.action = Confirm Abort Action
caption.metadata.popup = Metadata of
caption.metadata.delete.action.confirmbox = Confirm Metadata Delete Action
# Labels prefix with - label
label.dist.details.type = Type :
label.dist.details.name = Name :
label.dist.details.version = Version :
label.dist.details.vendor = Vendor :
label.dist.details.jvm = Runtime :
label.dist.details.ah = Application :
label.dist.details.os = OS :
label.modified.date = Last modified at :
label.modified.by = Last modified by :
label.created.at = Created at :
label.created.by = Created by :
label.target.count = Targets :
label.description = Description :
label.ip = Address :
label.type = Type :
label.assigned.type = Assignment type :
label.assigned.count = {0} Assigned
label.installed.count = {0} Installed
label.mandatory.field = * Mandatory Field
label.components.drop.area = Drop here to delete
label.software.module.drop.area = Delete Software
label.create.tag = Create Tag
label.update.tag = Update Tag
label.create.type = Create Type
label.update.type = Update Type
label.singleAssign.type = Firmware (FW)
label.multiAssign.type = Software (SW)
label.choose.type = Choose Type
label.choose.type.color = Choose Type Color
label.combobox.type = Select Type
label.combobox.tag = Select Tag
label.choose.tag = Choose Tag to update
label.choose.tag.color = Choose Tag Color
label.filter = Filter:
label.target.filter.count = Total Targets:
label.target.filtered.total = Total filtered targets :
label.filter.selected = Selected:
label.filter.shown = Shown:
label.filter = Filter :
label.target.filter.count = Total Targets :
label.filter.selected = Selected :
label.filter.shown = Shown :
label.filter.status = Status,
label.filter.tags = Tags,
label.filter.text = Search Text
label.filter.dist = Distribution,
label.filter.custom = Custom
label.target.filter.truncated={0} targets has been truncated in the list due the target size limit of {1}, use filters to reduce the targets to be shown
label.active =Active
label.inactive = In-active
label.finished = Finished
label.error = Error
label.warning = Warning
label.running = Running
label.cancelled = Cancelled
label.cancelling = Canceling
label.retrieved = Retrieved
label.download = Downloading
label.scheduled = Scheduled
label.target.id = Controller Id :
label.target.ip = Controller IP :
label.target.security.token = Security token :
label.filter.by.status = Filter by Status
label.target.controller.attrs = <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.delete.module = Select and delete Software Module
tooltip.forced.item=Forced update action
tooltip.soft.item=Soft update action which interacts with an user as a attempt update action for example
tooltip.timeforced.item=Soft update until a specific time and then the action will be forced
tooltip.check.for.mandatory=Check to make Mandatory
tooltip.artifact.icon=Show Artifact Details
tooltip.click.to.edit = Click to edit
tooltip.metadata.icon = Manage Metadata
# Notification messages prefix with - message
message.save.success = {0} saved successfully
message.update.success = {0} updated successfully
message.delete.success = {0} deleted successfully
message.dist.installedorassigned = Target {targId} is already assigned/installed with distribution
message.dist.pending.action = Target {0} is already assigned with distribution {1} . Pending for action
message.empty.target.tags= No Tags Created
message.empty.disttype.tags = No Distribution type tags created
message.select.row = Please select a row to drag
message.error = Unknown error occured during the operation. Please contact administrator
message.dist.assigned.one = {0} is assigned to {1}
message.dist.assigned.many = {0} DistributionSets are assigned to {1}
message.dist.unassigned.one = {0} is unassigned from {1}
message.dist.unassigned.many = {0} DistributionSets are unassigned from {1}
message.target.assigned.one = {0} is assigned to {1}
message.target.assigned.many = {0} Targets are assigned to {1}
message.target.unassigned.one = {0} is unassigned from {1}
message.target.unassigned.many = {0} Targets are unassigned from {1}
message.target.assigned.pending = Some target(s) are already assigned.Pending for action
message.cannot.delete = Cannot be deleted
message.check.softwaremodule = Please provide both name and version!
message.cannot.delete.default.dstype = Default distribution set type cannot be deleted
message.duplicate.softwaremodule = {0} : {1} already exists!
message.cannot.delete.default.dstype = Default distribution set type cannot be deleted
message.tag.delete = Please unclick the tag {0}, then try to delete
message.dist.type.check.delete = Please unclick the distribution type {0}, then try to delete
message.swmodule.type.check.delete = Please unclick the Software Module type {0}, then try to delete
message.targets.already.deleted = Few Target(s) are already deleted.Pending for action
message.dists.already.deleted = Few distribution(s) are already deleted.Pending for action
message.target.deleted.pending = Target(s) already deleted.Pending for action
message.dist.deleted.pending = Distribution(s) already deleted.Pending for action
message.dist.delete.success = All selected distribution sets are deleted successfully !
message.dist.discard.success = All distributions selected for delete are discarded successfully !
message.target.delete.success = All selected targets are deleted successfully !
message.target.discard.success = All targets selected for delete are discarded successfully !
message.software.discard.success = All software modules selected for delete are discarded successfully !
message.software.type.discard.success = All software moduleTypes selected for delete are discarded successfully !
message.assign.software.discard.success = All software moduleTypes selected for assign are discarded successfully !
message.software.delete.success = All software modules selected for delete are deleted successfully !
message.software.type.delete.success = All software modules types selected for delete are deleted successfully !
message.dist.set.type.deleted.success = {0} DistributionSetType deleted successfully !
message.new.dist.save.success = {0} - {1} saved successfully
message.dist.update.success = {0} - {1} updated successfully
message.duplicate.dist = Distribution set [{0}] or version [{1}] must be unique, entered value already exists.
message.error.view = No such view: {0}
message.accessdenied.view = No access to view: {0}
message.no.data = No data available
message.target.assignment = {0} Assignment(s) done
message.target.deleted = {0} Target(s) deleted
message.dist.deleted = {0} Distribution Set(s) deleted
message.tag.update.mandatory = Please select the Tag to update
message.tag.duplicate.check = {0} already exists, please enter another value
message.type.key.duplicate.check = Distribution type with key {0} already exists, please give another value
message.type.key.swmodule.duplicate.check = Software Module type with key {0} already exists, please give another value
message.no.action.history = No action history is available for the target : {0}
message.no.available = --No messages available--
message.no.actionupdateds.available = No other updates available for this action
message.mandatory.check = Mandatory details are missing
message.target.duplicate.check = Target [ {0} ] must be unique, entered value already exists.
message.permission.insufficient = Insufficient permissions to perform this action.
message.error.temp = The operation cannot be fulfilled due to {0}. Please contact administrator
message.dist.alreadyassigned = {0} : {1} is already assigned/installed, cannot be updated
message.dist.tag.alreadyassigned = {0} : {1} is already assigned/installed, cannot assign/un-assign to tag
message.dists.unassign.tag.alreadyassigned = Few of the DistributionSet's are already assigned to Target, those cannot be unassigned from Tag"
message.dists.assign.tag.alreadyassigned = Few of the DistributionSet's are already assigned to Target, those cannot be assigned to Tag"
message.dists.tag.assigned = {0} DistributionSet's assigned to Tag {1}
message.dists.tag.unassigned = {0} DistributionSet's un-assigned from Tag {1}
message.dist.no.operation = {0} - already assigned/installed, No operation
message.sm.delete.confirm = Are you sure that you want to delete the selected {0} Software Module?
message.error.os.softmodule = Please select the OS to delete
message.error.ah.softmodule = Please select the Application to delete
message.error.softmodule.deleted = The selected Software Module is already deleted
message.cancel.action = Cancel
message.cancel.action.success = Action cancelled successfully !
message.cancel.action.failed = Unable to cancel the action !
message.cancel.action.confirm = Are you sure that you want to cancel this action?
message.target.alreadyAssigned = {0} Target(s) were already assigned
message.dist.alreadyAssigned = {0} Distribution Set(s) were already assigned
message.force.action = Force
message.force.action.confirm = Are you sure that you want to force this action?
message.force.action.success = Action forced successfully !
message.distribution.no.update = distribution {0} set is already assigned to targets and cannot be changed
message.action.not.allowed = Action not allowed
message.action.did.not.work = Action did not work. Please try again.
message.onlyone.distribution.assigned = Only one distribution set can be assigned
message.onlyone.distribution.dropallowed = Only one distribution set can be dropped
message.error.missing.typename = Missing Type Name
message.error.missing.typenameorkey = Missing Type Name or Key
message.tag.cannot.be.assigned = Target/DS cannot be assigned to {0}
message.no.targets.assiged.fortag = No targets are assigned to tag {0}
message.error.missing.tagname = Please select tag name
message.type.delete = Please unclick the distribution type {0}, then try to delete
message.error.dist.set.type.update= Distribution Set Type is already assigned to targets and cannot be changed
message.target.ds.assign.success = Assignments saved successfully !
message.no.directory.upload = Directory upload is not supported
message.delete.filter.confirm = Are you sure that you want to delete custom filter?
message.delete.filter.success = Custom filter {0} deleted Successfully!
message.create.filter.success = Custom filter {0} created Successfully!
message.update.filter.success = Custom filter updated Successfully!
message.target.filter.validation = Please enter name and query
message.target.filter.duplicate = {0} already exists, please enter another value
message.tag.use.bulk.upload = {0} cannot be deleted .It is in use in targets bulk upload
message.bulk.upload.tag.assignment.failed = Tag {0} assignment failed as tag no longer exists
message.bulk.upload.tag.assignments.failed= Few tag assignments failed as tags no longer exists
#reused messages
soft.module.jvm =Runtime
soft.module.application =Application
soft.module.os =OS
#Artifact upload
message.error.noFileSelected = No file selected for upload
message.error.noProvidedName = Please provide custom file name
message.error.noSwModuleSelected = Please select a Software Module
message.no.duplicateFiles = Duplicate files selected
message.no.duplicateFile = Duplicate file selected :
message.delete.artifact = Are you sure that you want to delete artifact {0} ?
message.duplicate.filename = Duplicate file name
message.swModule.deleted = {0} Software Module(s) deleted
message.error.missing.tagname = Please select tag name
message.upload.failed = Streaming Failed
message.uploadedfile.size.exceeded = File size exceeded .Allowed size {0} bytes
message.uploadedfile.aborted = File upload aborted
message.file.not.found = File not found
message.artifact.deleted =Artifact with file {0} deleted successfully
message.abort.upload = Are you sure that you want to abort the upload?
upload.swModuleTable.header = Software Module
upload.selectedfile.name = file selected for upload
upload.file.name = File name
upload.sha1 = SHA1 checksum
upload.md5 = MD5 checksum
upload.last.modified.date=Last modified date
upload.failed = Failed
upload.success = Success
upload.caption.add.new.swmodule = Configure New Software Module
upload.caption.delete.swmodule = Configure Delete Software Module
upload.swmodule.type = Type
upload.artifact.alreadyExists = Artifact will be overridden as the given name already exists
upload.size = Size(B)
upload.validation = Validation
upload.reason = Reason
upload.action = Action
upload.result.status = Upload status
upload.file = Upload File
upload.caption.update.swmodule = Update Software Module
caption.tab.details = Details
caption.tab.description = Description
caption.delete.artifact.confirmbox = Confirm Artifact Delete Action
#Manage distributions view
label.drop.dist.delete.area = Drop here<br>to delete
caption.assign.software.dist.accordion.tab = Assign Software Modules
message.software.assignment = {0} Assignment(s) done
message.dist.inuse = {0} Distribution is already assigned to target
message.software.dist.already.assigned = {0} Distribution already has Software Module {1}
message.software.dist.type.notallowed = {0} Software Module type can not assign to Distribution {1}
message.target.assigned = {0} is assigned to {1}
message.dist.type.delete = {0} DistributionType(s) Deleted successfully.
message.sw.module.type.delete = {0} Software Module Type(s) deleted successfully.
message.dist.type.discard.success = All Distribution Types are discarded successfully !
message.dist.discard.success = All Distributions are discarded successfully !
message.assign.discard.success = All assignments are discarded successfully !
message.bulk.upload.assignment.failed = Distribution set assignment failed as distribution set no longer exists!
message.key.missing = Key is missing !
message.value.missing = Value is missing !
message.metadata.saved = Metadata with key {0} successfully saved !
message.metadata.updated = Metadata with key {0} successfully updated !
message.metadata.duplicate.check = Metadata with key {0} already exists, please enter another value
message.metadata.deleted.successfully = Metadata with key {0} successfully deleted !
message.confirm.delete.metadata = Are you sure that you want to delete metadata with key {0} ?
message.error.notification.ds.target.assigned = Distribution set {0}:{1} is already assigned to targets and cannot be changed
# Login view
notification.login.title=Welcome to Bosch IoT Software Provisioning.
notification.login.description=Please login with your Bosch Identity Management credentials.
notification.login.failed.title=Login failed!
notification.login.failed.description=Login with the given credentials failed.
notification.login.failed.credentialsexpired.title=Passwort Abgelaufen!
notification.login.failed.credentialsexpired.description=Passwort ist entweder abgelaufen or muss initial gesetzt werden, bitte im Benutzer Verwaltungssystem <20>ndern.
label.login.tenant=Tenant
label.login.username=Username
label.login.password=Password
button.login.signin=Sign in
checkbox.login.rememberme=Remember me
# Links
link.documentation.name=Dokumentation
link.demo.name=Demo
link.requestaccount.name=Beantragung eines Accounts
link.support.name=Support
link.usermanagement.name=Benutzerverwaltung
# System Configuration View
notification.configuration.save=Ver<EFBFBD>nderungen gespeichert
configuration.defaultdistributionset.title=Distribution Set Typ
configuration.defaultdistributionset.select.label=Wahl des default Distribution Set typs:
configuration.savebutton.tooltip=Konfigurationen speichern
configuration.cancellbutton.tooltip=Konfigurationen zur<75>cksetzen
configuration.authentication.title=Authentifikationseinstellungen
#Calendar
calendar.year=Jahr
calendar.years=Jahre
calendar.month=Monat
calendar.months=Monate
calendar.day=Tag
calendar.days=Tage
calendar.hour=Stunde
calendar.hours=Stunden
calendar.minute=Minute
calendar.minutes=Minuten
calendar.second=Sekunde
calendar.seconds=Sekunden
header.name = Name
header.vendor = Vendor
header.version = Version
header.description = Description
header.createdBy = Created By
header.createdDate = Created Date
header.modifiedBy = Modified By
header.modifiedDate = Modified Date
header.delete = Delete
header.assigned.ds = Assigned DS
header.installed.ds = Installed DS
header.status = Status
header.target.tags = Tags
header.distributionset = Distribution set
header.numberofgroups = No. of groups
header.detail.status = Detail status
header.total.targets = Total targets
header.type = Type
header.swmodules = SwModules
header.migrations.step=IsRequiredMigrationStep
header.action=Actions
header.action.run=Run
header.action.pause=Pause
header.action.update=Edit
header.rolloutgroup.installed.percentage = % Finished
header.rolloutgroup.threshold.error = Error threshold
header.rolloutgroup.threshold = Trigger threshold
header.rolloutgroup.target.date = Date and time
header.rolloutgroup.target.message = Messages
rollout.group.label.target.truncated = {0} targets has been truncated in the list due the target size limit of {1}
distribution.details.header = Distribution Set
target.details.header = Target
header.caption.mandatory = Mandatory
header.caption.typename = SoftwareModuleType
header.caption.softwaremodule = SoftwareModule
header.caption.unassign = Unassign
message.sw.unassigned = Software Module {0} successfully unassigned
header.caption.upload.details = Upload details
header.key = Key
header.value = Value
combo.type.tag.name = Type tag name
label.yes = Yes
label.no =No
#Menu
menu.title = Software Provisioning
#Rollout management
prompt.number.of.groups = Number of groups
prompt.tigger.threshold = Trigger threshold
prompt.error.threshold = Error threshold
prompt.distribution.set = Distribution Set
caption.configure.rollout = Configure Rollout
caption.update.rollout = Update Rollout
prompt.target.filter = Custom Target Filter
message.rollout.nonzero.group.number = Number of groups must be greater than zero
message.rollout.max.group.number = Number of groups must not be greater than 500
message.rollout.duplicate.check = Rollout [ {0} ] must be unique, entered value already exists.
message.rollout.field.value.range = Value should be in range {0} to {1}
message.correct.invalid.value = Please correct invalid values
message.enter.number = Please enter number
message.rollout.started = Rollout {0} started successfully
message.rollout.paused = Rollout {0} paused successfully
message.rollout.resumed = Rollout {0} resumed successfully
message.rollout.noofgroups.or.targetfilter.missing = Please enter number of groups and select target filter
message.rollouts = Rollouts
label.target.per.group = Targets per group :
message.dist.already.assigned = Distribution {0} is already assigned to target
message.error.creating.rollout = Server error. Error creating Rollout. Please contact the administrator
message.error.starting.rollout = Server error. Error starting Rollout. Please contact the administrator
#Target Filter Management
breadcrumb.target.filter.custom.filters = Custom Filters

View File

@@ -1,501 +0,0 @@
#
# Copyright (c) 2015 Bosch Software Innovations GmbH and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v1.0
# which accompanies this distribution, and is available at
# http://www.eclipse.org/legal/epl-v10.html
#
#########################################################################################
# This is the messages_en.properties file
#########################################################################################
# Button names prefix with - button
button.save = Save
button.delete = Delete
button.discard = Discard
button.discard.all = Discard All
button.delete.all = Delete All
button.assign.all = Save Assign
button.actions = You have actions
button.no.actions = No actions
button.ok = OK
button.cancel = Cancel
button.upload.file = Upload File
bulk.targets.upload = Please upload csv file.
bulkupload.ds.name = DS Name
# Headers prefix with - header
header.target.table=Targets
header.dist.table=Distributions
header.filter.tag=Filter by Tag
header.target.filter.tag=Filters
header.first.assignment.table = Targets
header.second.assignment.table = Distributions
header.dist.first.assignment.table = Distributions
header.dist.second.assignment.table = Software Modules
header.third.assignment.table = Discard
header.one.deletedist.table = Distribution Name
header.second.deletedist.table = Discard Changes
header.first.deletetarget.table = Target Name
header.second.deletetarget.table = Discard Changes
header.first.deleteswmodule.table = Delete software
header.first.delete.dist.type.table = DistributionSetType
header.second.delete.dist.type.table = Discard
header.first.delete.swmodule.type.table = Software Module Type
header.second.delete.swmodule.type.table = Discard
header.dist.twintable.selected=Selected
header.dist.twintable.available=Available
header.target.installed = Installed
header.target.assigned = Assigned
# Captions prefix with - caption
caption.action.history = Action history for {0}
caption.error = Error
caption.new.softwaremodule.application = Configure New Application
caption.new.softwaremodule.jvm = Configure New Runtime
caption.new.softwaremodule.os = Configure New OS
caption.filter.simple = Simple Filter
caption.filter.custom = Custom Filter
caption.metadata = Metadata
caption.add.softwaremodule = Configure Software Module
caption.add.new.dist = Configure New Distribution
caption.update.dist = Configure Update Distribution
caption.add.tag = Configure Tag
caption.add.type = Configure Type
caption.add.new.target = Configure New Target
caption.update.target = Configure Update Target
caption.bulk.upload.targets = Bulk Upload
caption.softwares.distdetail.tab = Modules
caption.tags.tab = Tags
caption.logs.tab = Logs
caption.attributes.tab = Attributes
caption.types.tab = Types
caption.save.window = Action Details
caption.assign.dist.accordion.tab = Assign Software Module
caption.delete.dist.accordion.tab = Delete Distributions
caption.delete.target.accordion.tab = Delete Targets
caption.delete.swmodule.accordion.tab = Delete SW Modules
caption.delete.dist.set.type.accordion.tab = Delete Distribution Set Type
caption.delete.sw.module.type.accordion.tab = Delete Software Module Type
caption.attributes = Attributes
caption.panel.dist.installed = Installed Distribution Set
caption.panel.dist.assigned = Assigned Distribution Set
caption.soft.delete.confirmbox = Confirm Software Module Delete Action
caption.cancel.action.confirmbox = Confirm Action Cancellation
caption.forced.datefield = Force update at time
caption.force.action.confirmbox = Confirm Force Active Action
caption.filter.delete.confirmbox = Confirm Filter Delete Action
caption.metadata.popup = Metadata of
caption.metadata.delete.action.confirmbox = Confirm Metadata Delete Action
caption.confirm.abort.action = Confirm Abort Action
# Labels prefix with - label
label.dist.details.type = Type :
label.dist.details.name = Name :
label.dist.details.version = Version :
label.dist.details.vendor = Vendor :
label.dist.details.jvm = Runtime :
label.dist.details.ah = Application :
label.dist.details.os = OS :
label.modified.date = Last modified at :
label.modified.by = Last modified by :
label.created.at = Created at :
label.created.by = Created by :
label.target.count = Targets :
label.description = Description :
label.ip = Address :
label.type = Type :
label.assigned.type = Assignment type :
label.assigned.count = {0} Assigned
label.installed.count = {0} Installed
label.mandatory.field = * Mandatory Field
label.components.drop.area = Drop here to delete
label.software.module.drop.area = Delete Software
label.create.tag = Create Tag
label.update.tag = Update Tag
label.create.type = Create Type
label.update.type = Update Type
label.singleAssign.type = Firmware (FW)
label.multiAssign.type = Software (SW)
label.choose.type = Choose Type to Update
label.choose.type.color = Choose Type Color
label.combobox.type = Select Type
label.combobox.tag = Select Tag
label.choose.tag = Choose Tag to update
label.choose.tag.color = Choose Tag Color
label.filter = Filter:
label.target.filter.count = Total Targets:
label.target.filtered.total = Total filtered targets :
label.filter.selected = Selected:
label.filter.shown = Shown:
label.filter.targets = Filtered targets :
label.filter = Filter :
label.target.filter.count = Total Targets :
label.filter.selected = Selected :
label.filter.shown = Shown :
label.filter.status = Status,
label.filter.tags = Tags,
label.filter.text = Search Text
label.filter.dist = Distribution,
label.filter.custom = Custom
label.target.filter.truncated={0} targets has been truncated in the list due the target size limit of {1}, use filters to reduce the targets to be shown
label.active =Active
label.inactive = In-active
label.finished = Finished
label.error = Error
label.warning = Warning
label.running = Running
label.cancelled = Cancelled
label.cancelling = Canceling
label.retrieved = Retrieved
label.download = Downloading
label.scheduled = Scheduled
label.target.id = Controller Id :
label.target.ip = Controller IP :
label.target.security.token = Security token :
label.filter.by.status = Filter by Status
label.target.controller.attrs = <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.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