Unify target attributes and metadata (#2408)
* Unify target attributes and metadata Currently, the target attributes are Map while the metadata, which has the same concept is List. This PR unifies them making the metadata also a Map Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -9,7 +9,6 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository;
|
||||
|
||||
import java.util.AbstractMap.SimpleImmutableEntry;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
@@ -41,7 +40,7 @@ public enum TargetFields implements RsqlQueryField {
|
||||
DistributionSetFields.NAME.getJpaEntityFieldName(), DistributionSetFields.VERSION.getJpaEntityFieldName()),
|
||||
TAG("tags", TagFields.NAME.getJpaEntityFieldName()),
|
||||
LASTCONTROLLERREQUESTAT("lastTargetQuery"),
|
||||
METADATA("metadata", new SimpleImmutableEntry<>("key", "value")),
|
||||
METADATA("metadata"),
|
||||
TARGETTYPE("targetType", TargetTypeFields.KEY.getJpaEntityFieldName(), TargetTypeFields.NAME.getJpaEntityFieldName());
|
||||
|
||||
private final String jpaEntityFieldName;
|
||||
@@ -56,10 +55,6 @@ public enum TargetFields implements RsqlQueryField {
|
||||
this(jpaEntityFieldName, List.of(subEntityAttributes), null);
|
||||
}
|
||||
|
||||
TargetFields(final String jpaEntityFieldName, final Entry<String, String> subEntityMapTuple) {
|
||||
this(jpaEntityFieldName, Collections.emptyList(), subEntityMapTuple);
|
||||
}
|
||||
|
||||
TargetFields(final String jpaEntityFieldName, final List<String> subEntityAttributes, final Entry<String, String> subEntityMapTuple) {
|
||||
this.jpaEntityFieldName = jpaEntityFieldName;
|
||||
this.subEntityAttributes = subEntityAttributes;
|
||||
@@ -73,6 +68,6 @@ public enum TargetFields implements RsqlQueryField {
|
||||
|
||||
@Override
|
||||
public boolean isMap() {
|
||||
return this == ATTRIBUTE || getSubEntityMapTuple().isPresent();
|
||||
return this == ATTRIBUTE || this == METADATA;
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2018 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* Sort fields for TargetMetadata.
|
||||
*/
|
||||
@Getter
|
||||
public enum TargetMetadataFields implements RsqlQueryField {
|
||||
|
||||
KEY("key"),
|
||||
VALUE("value");
|
||||
|
||||
private final String jpaEntityFieldName;
|
||||
|
||||
TargetMetadataFields(final String jpaEntityFieldName) {
|
||||
this.jpaEntityFieldName = jpaEntityFieldName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String identifierFieldName() {
|
||||
return KEY.getJpaEntityFieldName();
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* The representation of an meta data in the REST API for POST/Create.
|
||||
* The representation of a meta-data in the REST API for POST/Create.
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@@ -29,9 +29,9 @@ import lombok.experimental.Accessors;
|
||||
public class MgmtMetadata {
|
||||
|
||||
@JsonProperty(required = true)
|
||||
@Schema(description = "Metadata property key", example = "someKnownKey")
|
||||
@Schema(description = "Metadata property key", example = "country")
|
||||
private String key;
|
||||
|
||||
@Schema(description = "Metadata property value", example = "someKnownKeyValue")
|
||||
@Schema(description = "Metadata property value", example = "DE")
|
||||
private String value;
|
||||
}
|
||||
@@ -38,6 +38,7 @@ import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTargetAutoConfirmUpdate;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTargetRequestBody;
|
||||
import org.eclipse.hawkbit.rest.json.model.ExceptionInfo;
|
||||
import org.springframework.hateoas.MediaTypes;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
@@ -47,6 +48,7 @@ import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
/**
|
||||
* API for handling target operations.
|
||||
@@ -727,15 +729,47 @@ public interface MgmtTargetRestApi {
|
||||
produces = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
|
||||
ResponseEntity<List<MgmtTag>> getTags(@PathVariable("targetId") String targetId);
|
||||
|
||||
/**
|
||||
* Creates a list of metadata for a specific target.
|
||||
*
|
||||
* @param targetId the ID of the targetId to create metadata for
|
||||
* @param metadataRest the list of metadata entries to create
|
||||
*/
|
||||
@Operation(summary = "Create a list of metadata for a specific target", description = "Create a list of metadata entries Required permissions: READ_REPOSITORY and UPDATE_TARGET")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "201", description = "Successfully created"),
|
||||
@ApiResponse(responseCode = "400", description = "Bad Request - e.g. invalid parameters",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ExceptionInfo.class))),
|
||||
@ApiResponse(responseCode = "401", description = "The request requires user authentication.",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||
@ApiResponse(responseCode = "403",
|
||||
description = "Insufficient permissions, entity is not allowed to be changed (i.e. read-only) or " +
|
||||
"data volume restriction applies.",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||
@ApiResponse(responseCode = "404", description = "Target not found", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||
@ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||
@ApiResponse(responseCode = "406", description = "In case accept header is specified and not application/json.",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||
@ApiResponse(responseCode = "409", description = "E.g. in case an entity is created or modified by another " +
|
||||
"user in another request at the same time. You may retry your modification request.",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||
@ApiResponse(responseCode = "415", description = "The request was attempt with a media-type which is not " +
|
||||
"supported by the server for this resource.",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||
@ApiResponse(responseCode = "429", description = "Too many requests. The server will refuse further attempts " +
|
||||
"and the client has to wait another second.",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true)))
|
||||
})
|
||||
@PostMapping(value = MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/metadata",
|
||||
consumes = { MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE })
|
||||
@ResponseStatus(HttpStatus.CREATED)
|
||||
void createMetadata(@PathVariable("targetId") String targetId, @RequestBody List<MgmtMetadata> metadataRest);
|
||||
|
||||
/**
|
||||
* Gets a paged list of metadata for a target.
|
||||
*
|
||||
* @param targetId the ID of the target for the metadata
|
||||
* @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=key==abc}
|
||||
* @return status OK if get request is successful with the paged list of metadata
|
||||
*/
|
||||
@Operation(summary = "Return metadata for specific target", description = "Get a paged list of metadata for a target. Required permission: READ_REPOSITORY")
|
||||
@@ -758,32 +792,9 @@ public interface MgmtTargetRestApi {
|
||||
"and the client has to wait another second.",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true)))
|
||||
})
|
||||
@GetMapping(value = MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/metadata", produces = {
|
||||
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
|
||||
ResponseEntity<PagedList<MgmtMetadata>> getMetadata(
|
||||
@PathVariable("targetId") String targetId,
|
||||
@RequestParam(
|
||||
value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET,
|
||||
defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET)
|
||||
@Schema(description = "The paging offset (default is 0)")
|
||||
int pagingOffsetParam,
|
||||
@RequestParam(
|
||||
value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT,
|
||||
defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT)
|
||||
@Schema(description = "The maximum number of entries in a page (default is 50)")
|
||||
int pagingLimitParam,
|
||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false)
|
||||
@Schema(description = """
|
||||
The query parameter sort allows to define the sort order for the result of a query. A sort criteria
|
||||
consists of the name of a field and the sort direction (ASC for ascending and DESC descending).
|
||||
The sequence of the sort criteria (multiple can be used) defines the sort order of the entities
|
||||
in the result.""")
|
||||
String sortParam,
|
||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false)
|
||||
@Schema(description = """
|
||||
Query fields based on the Feed Item Query Language (FIQL). See Entity Definitions for
|
||||
available fields.""")
|
||||
String rsqlParam);
|
||||
@GetMapping(value = MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/metadata",
|
||||
produces = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
|
||||
ResponseEntity<PagedList<MgmtMetadata>> getMetadata(@PathVariable("targetId") String targetId);
|
||||
|
||||
/**
|
||||
* Gets a single metadata value for a specific key of a target.
|
||||
@@ -824,7 +835,6 @@ public interface MgmtTargetRestApi {
|
||||
* @param targetId the ID of the target to update the metadata entry
|
||||
* @param metadataKey the key of the metadata to update the value
|
||||
* @param metadata update body
|
||||
* @return status OK if the update request is successful and the updated metadata result
|
||||
*/
|
||||
@Operation(summary = "Updates a single metadata value of a target", description = "Update a single metadata value for speficic key. Required permission: UPDATE_REPOSITORY")
|
||||
@ApiResponses(value = {
|
||||
@@ -834,8 +844,7 @@ public interface MgmtTargetRestApi {
|
||||
@ApiResponse(responseCode = "401", description = "The request requires user authentication.",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||
@ApiResponse(responseCode = "403",
|
||||
description = "Insufficient permissions, entity is not allowed to be changed (i.e. read-only) or " +
|
||||
"data volume restriction applies.",
|
||||
description = "Insufficient permissions, entity is not allowed to be changed (i.e. read-only) or data volume restriction applies.",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||
@ApiResponse(responseCode = "404", description = "Target not found.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||
@ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.",
|
||||
@@ -852,9 +861,9 @@ public interface MgmtTargetRestApi {
|
||||
"and the client has to wait another second.",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true)))
|
||||
})
|
||||
@PutMapping(value = MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/metadata/{metadataKey}",
|
||||
produces = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
|
||||
ResponseEntity<MgmtMetadata> updateMetadata(
|
||||
@PutMapping(value = MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/metadata/{metadataKey}")
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
void updateMetadata(
|
||||
@PathVariable("targetId") String targetId,
|
||||
@PathVariable("metadataKey") String metadataKey,
|
||||
@RequestBody MgmtMetadataBodyPut metadata);
|
||||
@@ -887,49 +896,8 @@ public interface MgmtTargetRestApi {
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true)))
|
||||
})
|
||||
@DeleteMapping(value = MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/metadata/{metadataKey}")
|
||||
ResponseEntity<Void> deleteMetadata(
|
||||
@PathVariable("targetId") String targetId,
|
||||
@PathVariable("metadataKey") String metadataKey);
|
||||
|
||||
/**
|
||||
* Creates a list of metadata for a specific target.
|
||||
*
|
||||
* @param targetId the ID of the targetId to create metadata for
|
||||
* @param metadataRest the list of metadata entries to create
|
||||
* @return status created if post request is successful with the value of the created metadata
|
||||
*/
|
||||
@Operation(summary = "Create a list of metadata for a specific target", description = "Create a list of metadata entries Required permissions: READ_REPOSITORY and UPDATE_TARGET")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "201", description = "Successfully created"),
|
||||
@ApiResponse(responseCode = "400", description = "Bad Request - e.g. invalid parameters",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ExceptionInfo.class))),
|
||||
@ApiResponse(responseCode = "401", description = "The request requires user authentication.",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||
@ApiResponse(responseCode = "403",
|
||||
description = "Insufficient permissions, entity is not allowed to be changed (i.e. read-only) or " +
|
||||
"data volume restriction applies.",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||
@ApiResponse(responseCode = "404", description = "Target not found", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||
@ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||
@ApiResponse(responseCode = "406", description = "In case accept header is specified and not application/json.",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||
@ApiResponse(responseCode = "409", description = "E.g. in case an entity is created or modified by another " +
|
||||
"user in another request at the same time. You may retry your modification request.",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||
@ApiResponse(responseCode = "415", description = "The request was attempt with a media-type which is not " +
|
||||
"supported by the server for this resource.",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||
@ApiResponse(responseCode = "429", description = "Too many requests. The server will refuse further attempts " +
|
||||
"and the client has to wait another second.",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true)))
|
||||
})
|
||||
@PostMapping(value = MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/metadata",
|
||||
consumes = { MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE },
|
||||
produces = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
|
||||
ResponseEntity<List<MgmtMetadata>> createMetadata(
|
||||
@PathVariable("targetId") String targetId,
|
||||
@RequestBody List<MgmtMetadata> metadataRest);
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
void deleteMetadata(@PathVariable("targetId") String targetId, @PathVariable("metadataKey") String metadataKey);
|
||||
|
||||
/**
|
||||
* Get the current auto-confirm state for a specific target.
|
||||
@@ -945,8 +913,7 @@ public interface MgmtTargetRestApi {
|
||||
@ApiResponse(responseCode = "401", description = "The request requires user authentication.",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||
@ApiResponse(responseCode = "403",
|
||||
description = "Insufficient permissions, entity is not allowed to be changed (i.e. read-only) or " +
|
||||
"data volume restriction applies.",
|
||||
description = "Insufficient permissions, entity is not allowed to be changed (i.e. read-only) or data volume restriction applies.",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||
@ApiResponse(responseCode = "404", description = "Target not found.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||
@ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.",
|
||||
|
||||
@@ -18,7 +18,9 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
@@ -46,11 +48,9 @@ import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
||||
import org.eclipse.hawkbit.repository.model.AutoConfirmationStatus;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.MetaData;
|
||||
import org.eclipse.hawkbit.repository.model.PollStatus;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetMetadata;
|
||||
import org.eclipse.hawkbit.rest.json.model.ResponseList;
|
||||
import org.eclipse.hawkbit.util.IpUtil;
|
||||
import org.eclipse.hawkbit.utils.TenantConfigHelper;
|
||||
@@ -79,10 +79,8 @@ public final class MgmtTargetMapper {
|
||||
MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT_VALUE,
|
||||
ActionFields.ID.getJpaEntityFieldName() + ":" + SortDirection.DESC, null))
|
||||
.withRel(MgmtRestConstants.TARGET_V1_ACTIONS).expand());
|
||||
response.add(linkTo(methodOn(MgmtTargetRestApi.class).getMetadata(response.getControllerId(),
|
||||
MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET_VALUE,
|
||||
MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT_VALUE, null, null)).withRel("metadata")
|
||||
.expand());
|
||||
response.add(linkTo(methodOn(MgmtTargetRestApi.class).getMetadata(response.getControllerId()))
|
||||
.withRel("metadata").expand());
|
||||
if (response.getTargetType() != null) {
|
||||
response.add(linkTo(methodOn(MgmtTargetTypeRestApi.class).getTargetType(response.getTargetType()))
|
||||
.withRel(MgmtRestConstants.TARGET_V1_ASSIGNED_TARGET_TYPE).expand());
|
||||
@@ -196,19 +194,14 @@ public final class MgmtTargetMapper {
|
||||
.toList();
|
||||
}
|
||||
|
||||
static List<MetaData> fromRequestTargetMetadata(final List<MgmtMetadata> metadata,
|
||||
final EntityFactory entityFactory) {
|
||||
if (metadata == null) {
|
||||
return Collections.emptyList();
|
||||
static Map<String, String> fromRequestMetadata(final List<MgmtMetadata> metadata) {
|
||||
return metadata == null
|
||||
? Collections.emptyMap()
|
||||
: metadata.stream().collect(Collectors.toMap(MgmtMetadata::getKey, MgmtMetadata::getValue));
|
||||
}
|
||||
|
||||
return metadata.stream().map(
|
||||
metadataRest -> entityFactory.generateTargetMetadata(metadataRest.getKey(), metadataRest.getValue()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
static List<MgmtActionStatus> toActionStatusRestResponse(final Collection<ActionStatus> actionStatus,
|
||||
final DeploymentManagement deploymentManagement) {
|
||||
static List<MgmtActionStatus> toActionStatusRestResponse(
|
||||
final Collection<ActionStatus> actionStatus, final DeploymentManagement deploymentManagement) {
|
||||
if (actionStatus == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
@@ -308,15 +301,15 @@ public final class MgmtTargetMapper {
|
||||
return actions.stream().map(action -> toResponse(targetId, action)).toList();
|
||||
}
|
||||
|
||||
static MgmtMetadata toResponseTargetMetadata(final TargetMetadata metadata) {
|
||||
static MgmtMetadata toResponseMetadata(final String key, final String value) {
|
||||
final MgmtMetadata metadataRest = new MgmtMetadata();
|
||||
metadataRest.setKey(metadata.getKey());
|
||||
metadataRest.setValue(metadata.getValue());
|
||||
metadataRest.setKey(key);
|
||||
metadataRest.setValue(value);
|
||||
return metadataRest;
|
||||
}
|
||||
|
||||
static List<MgmtMetadata> toResponseTargetMetadata(final List<TargetMetadata> metadata) {
|
||||
return metadata.stream().map(MgmtTargetMapper::toResponseTargetMetadata).toList();
|
||||
static List<MgmtMetadata> toResponseMetadata(final Map<String, String> metadata) {
|
||||
return metadata.entrySet().stream().map(e -> toResponseMetadata(e.getKey(), e.getValue())).toList();
|
||||
}
|
||||
|
||||
private static void addPollStatus(final Target target, final MgmtTarget targetRest, final Function<Target, PollStatus> pollStatusResolver) {
|
||||
|
||||
@@ -57,7 +57,6 @@ import org.eclipse.hawkbit.repository.model.ActionStatus;
|
||||
import org.eclipse.hawkbit.repository.model.DeploymentRequest;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.TargetTag;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.utils.TenantConfigHelper;
|
||||
@@ -421,57 +420,39 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
|
||||
|
||||
@Override
|
||||
public ResponseEntity<List<MgmtTag>> getTags(final String targetId) {
|
||||
final Set<TargetTag> tags = targetManagement.getTagsByControllerId(targetId);
|
||||
final Set<TargetTag> tags = targetManagement.getTags(targetId);
|
||||
return ResponseEntity.ok(MgmtTagMapper.toResponse(tags == null ? Collections.emptyList() : tags.stream().toList()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<PagedList<MgmtMetadata>> getMetadata(
|
||||
final String targetId,
|
||||
final int pagingOffsetParam, final int pagingLimitParam, final String sortParam, final String rsqlParam) {
|
||||
final int sanitizedOffsetParam = PagingUtility.sanitizeOffsetParam(pagingOffsetParam);
|
||||
final int sanitizedLimitParam = PagingUtility.sanitizePageLimitParam(pagingLimitParam);
|
||||
final Sort sorting = PagingUtility.sanitizeDistributionSetMetadataSortParam(sortParam);
|
||||
|
||||
final Pageable pageable = new OffsetBasedPageRequest(sanitizedOffsetParam, sanitizedLimitParam, sorting);
|
||||
final Page<TargetMetadata> metaDataPage;
|
||||
|
||||
if (rsqlParam != null) {
|
||||
metaDataPage = targetManagement.findMetaDataByControllerIdAndRsql(pageable, targetId, rsqlParam);
|
||||
} else {
|
||||
metaDataPage = targetManagement.findMetaDataByControllerId(pageable, targetId);
|
||||
public void createMetadata(final String targetId, final List<MgmtMetadata> metadataRest) {
|
||||
targetManagement.createMetadata(targetId, MgmtTargetMapper.fromRequestMetadata(metadataRest));
|
||||
}
|
||||
|
||||
return ResponseEntity.ok(new PagedList<>(MgmtTargetMapper.toResponseTargetMetadata(
|
||||
metaDataPage.getContent()), metaDataPage.getTotalElements()));
|
||||
@Override
|
||||
public ResponseEntity<PagedList<MgmtMetadata>> getMetadata(final String targetId) {
|
||||
final Map<String, String> metadata = targetManagement.getMetadata(targetId);
|
||||
return ResponseEntity.ok(new PagedList<>(MgmtTargetMapper.toResponseMetadata(metadata), metadata.size()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<MgmtMetadata> getMetadataValue(final String targetId, final String metadataKey) {
|
||||
final TargetMetadata findOne = targetManagement.getMetaDataByControllerId(targetId, metadataKey)
|
||||
.orElseThrow(() -> new EntityNotFoundException(TargetMetadata.class, targetId, metadataKey));
|
||||
return ResponseEntity.ok(MgmtTargetMapper.toResponseTargetMetadata(findOne));
|
||||
final String metadataValue = targetManagement.getMetadata(targetId).get(metadataKey);
|
||||
if (metadataValue == null) {
|
||||
throw new EntityNotFoundException("Target metadata", targetId + ":" + metadataKey);
|
||||
}
|
||||
return ResponseEntity.ok(MgmtTargetMapper.toResponseMetadata(metadataKey, metadataValue));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<MgmtMetadata> updateMetadata(final String targetId, final String metadataKey, final MgmtMetadataBodyPut metadata) {
|
||||
final TargetMetadata updated = targetManagement.updateMetadata(targetId,
|
||||
entityFactory.generateTargetMetadata(metadataKey, metadata.getValue()));
|
||||
return ResponseEntity.ok(MgmtTargetMapper.toResponseTargetMetadata(updated));
|
||||
public void updateMetadata(final String targetId, final String metadataKey, final MgmtMetadataBodyPut metadata) {
|
||||
targetManagement.updateMetadata(targetId, metadataKey, metadata.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
@AuditLog(entity = "Target", type = AuditLog.Type.DELETE, description = "Delete Target Metadata")
|
||||
public ResponseEntity<Void> deleteMetadata(final String targetId, final String metadataKey) {
|
||||
targetManagement.deleteMetaData(targetId, metadataKey);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<List<MgmtMetadata>> createMetadata(final String targetId, final List<MgmtMetadata> metadataRest) {
|
||||
final List<TargetMetadata> created = targetManagement.createMetaData(targetId,
|
||||
MgmtTargetMapper.fromRequestTargetMetadata(metadataRest, entityFactory));
|
||||
return new ResponseEntity<>(MgmtTargetMapper.toResponseTargetMetadata(created), HttpStatus.CREATED);
|
||||
public void deleteMetadata(final String targetId, final String metadataKey) {
|
||||
targetManagement.deleteMetadata(targetId, metadataKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -37,7 +37,6 @@ import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -74,12 +73,10 @@ import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||
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.MetaData;
|
||||
import org.eclipse.hawkbit.repository.model.NamedEntity;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.TargetTag;
|
||||
import org.eclipse.hawkbit.repository.model.TargetType;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
@@ -114,8 +111,6 @@ import org.springframework.test.web.servlet.ResultActions;
|
||||
@Story("Target Resource")
|
||||
class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
ActionRepository actionRepository;
|
||||
private static final String TARGET_DESCRIPTION_TEST = "created in test";
|
||||
private static final String JSON_PATH_ROOT = "$";
|
||||
// fields, attributes
|
||||
@@ -139,23 +134,26 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
|
||||
private static final String JSON_PATH_DESCRIPTION = JSON_PATH_ROOT + JSON_PATH_FIELD_DESCRIPTION;
|
||||
private static final String JSON_PATH_LAST_REQUEST_AT = JSON_PATH_ROOT + JSON_PATH_FIELD_LAST_REQUEST_AT;
|
||||
private static final String JSON_PATH_TYPE = JSON_PATH_ROOT + JSON_PATH_FIELD_TARGET_TYPE;
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
ActionRepository actionRepository;
|
||||
@Autowired
|
||||
private JpaProperties jpaProperties;
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@Test
|
||||
@Description("Ensures that when targetType value of -1 is provided the target type is unassigned from the target.")
|
||||
public void updateTargetAndUnnasignTargetType() throws Exception {
|
||||
void updateTargetAndUnassignTargetType() throws Exception {
|
||||
final String knownControllerId = "123";
|
||||
final String knownNewAddress = "amqp://test123/foobar";
|
||||
final String knownNameNotModify = "controllerName";
|
||||
final Long unnasignTargetTypeValue = -1L;
|
||||
final Long unassignTargetTypeValue = -1L;
|
||||
|
||||
final TargetType targetType = targetTypeManagement.create(
|
||||
entityFactory.targetType().create().name("targettype1").description("targettypedes1"));
|
||||
|
||||
final String body = new JSONObject().put("targetType", unnasignTargetTypeValue).toString();
|
||||
final String body = new JSONObject().put("targetType", unassignTargetTypeValue).toString();
|
||||
|
||||
// create a target with the created TargetType
|
||||
targetManagement.create(entityFactory.target().create().controllerId(knownControllerId).name(knownNameNotModify)
|
||||
@@ -183,18 +181,18 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
|
||||
|
||||
@Test
|
||||
@Description("Ensures that when targetType value of -1 is provided the target type is unassigned from the target when updating multiple fields in target object.")
|
||||
public void updateTargetNameAndUnnasignTargetType() throws Exception {
|
||||
void updateTargetNameAndUnassignTargetType() throws Exception {
|
||||
final String knownControllerId = "123";
|
||||
final String knownNewAddress = "amqp://test123/foobar";
|
||||
final String knownNameNotModify = "controllerName";
|
||||
final Long unnasignTargetTypeValue = -1L;
|
||||
final Long unassignTargetTypeValue = -1L;
|
||||
final String controllerNewName = "controllerNewName";
|
||||
|
||||
final TargetType targetType = targetTypeManagement.create(
|
||||
entityFactory.targetType().create().name("targettype1").description("targettypedes1"));
|
||||
|
||||
final String body = new JSONObject()
|
||||
.put("targetType", unnasignTargetTypeValue).put("name", "controllerNewName")
|
||||
.put("targetType", unassignTargetTypeValue).put("name", "controllerNewName")
|
||||
.toString();
|
||||
|
||||
// create a target with the created TargetType
|
||||
@@ -223,7 +221,7 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
|
||||
|
||||
@Test
|
||||
@Description("Handles the GET request of retrieving all targets within SP..")
|
||||
public void getTargets() throws Exception {
|
||||
void getTargets() throws Exception {
|
||||
enableConfirmationFlow();
|
||||
mvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING))
|
||||
.andExpect(status().isOk())
|
||||
@@ -232,33 +230,15 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
|
||||
|
||||
@Test
|
||||
@Description("Handles the GET request of retrieving all targets within SP based by parameter.")
|
||||
public void getTargetsWithParameters() throws Exception {
|
||||
void getTargetsWithParameters() throws Exception {
|
||||
mvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "?limit=10&sort=name:ASC&offset=0&q=name==a"))
|
||||
.andExpect(status().isOk())
|
||||
.andDo(MockMvcResultPrinter.print());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Get a paged list of meta data for a target with standard page size.")
|
||||
public void getMetadata() throws Exception {
|
||||
final int totalMetadata = 4;
|
||||
final String knownKeyPrefix = "knownKey";
|
||||
final String knownValuePrefix = "knownValue";
|
||||
final Target testTarget = testdataFactory.createTarget("targetId");
|
||||
for (int index = 0; index < totalMetadata; index++) {
|
||||
targetManagement.createMetaData(testTarget.getControllerId(), List.of(
|
||||
entityFactory.generateTargetMetadata(knownKeyPrefix + index, knownValuePrefix + index)));
|
||||
}
|
||||
|
||||
mvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/metadata", testTarget.getControllerId()))
|
||||
.andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().contentType(MediaTypes.HAL_JSON));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Handles the POST request to activate auto-confirm on a target. Payload can be provided to specify more details about the operation.")
|
||||
public void postActivateAutoConfirm() throws Exception {
|
||||
void postActivateAutoConfirm() throws Exception {
|
||||
final Target testTarget = testdataFactory.createTarget("targetId");
|
||||
|
||||
final MgmtTargetAutoConfirmUpdate body = new MgmtTargetAutoConfirmUpdate("custom_initiator_value",
|
||||
@@ -273,7 +253,7 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
|
||||
|
||||
@Test
|
||||
@Description("Handles the POST request to deactivate auto-confirm on a target.")
|
||||
public void postDeactivateAutoConfirm() throws Exception {
|
||||
void postDeactivateAutoConfirm() throws Exception {
|
||||
final Target testTarget = testdataFactory.createTarget("targetId");
|
||||
confirmationManagement.activateAutoConfirmation(testTarget.getControllerId(), null, null);
|
||||
|
||||
@@ -285,7 +265,7 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
|
||||
|
||||
@Test
|
||||
@Description("Test confirmation of single Action with confirm status. Check that Action goes into Running status with appropriate messages and status code")
|
||||
public void updateActionConfirmationWithConfirm() throws Exception {
|
||||
void updateActionConfirmationWithConfirm() throws Exception {
|
||||
final int expectedStatusCode = 210;
|
||||
final String expectedStatusMessage1 = "some-custom-message1";
|
||||
final String expectedStatusMessage2 = "some-custom-message2";
|
||||
@@ -296,7 +276,7 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
|
||||
|
||||
@Test
|
||||
@Description("Test confirmation of single Action with deny status. Check that Action stays in WAIT_FOR_CONFIRMATION status with appropriate messages and status code")
|
||||
public void updateActionConfirmationWithDeny() throws Exception {
|
||||
void updateActionConfirmationWithDeny() throws Exception {
|
||||
final int expectedStatusCode = 410;
|
||||
final String expectedStatusMessage1 = "some-error-custom-message1";
|
||||
final String expectedStatusMessage2 = "some-error-custom-message2";
|
||||
@@ -307,7 +287,7 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
|
||||
|
||||
@Test
|
||||
@Description("Test confirmation of single Action with wrong ControllerId - e.g. the given Action is not assigned to the given Target - confirmation call must fail.")
|
||||
public void updateActionConfirmationFailsIfActionNotAssignedToTarget() throws Exception {
|
||||
void updateActionConfirmationFailsIfActionNotAssignedToTarget() throws Exception {
|
||||
final int payloadCallCode = 200;
|
||||
final String payloadCallMessage1 = "random1";
|
||||
final String payloadCallMessage2 = "random2";
|
||||
@@ -2005,17 +1985,10 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
|
||||
.contentType(MediaType.APPLICATION_JSON).content(metaData1.toString()))
|
||||
.andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isCreated())
|
||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
|
||||
.andExpect(jsonPath("[0].key", equalTo(knownKey1)))
|
||||
.andExpect(jsonPath("[0].value", equalTo(knownValue1)))
|
||||
.andExpect(jsonPath("[1].key", equalTo(knownKey2)))
|
||||
.andExpect(jsonPath("[1].value", equalTo(knownValue2)));
|
||||
.andExpect(content().string(""));
|
||||
|
||||
final TargetMetadata metaKey1 = targetManagement.getMetaDataByControllerId(knownControllerId, knownKey1).get();
|
||||
final TargetMetadata metaKey2 = targetManagement.getMetaDataByControllerId(knownControllerId, knownKey2).get();
|
||||
|
||||
assertThat(metaKey1.getValue()).isEqualTo(knownValue1);
|
||||
assertThat(metaKey2.getValue()).isEqualTo(knownValue2);
|
||||
assertThat(targetManagement.getMetadata(knownControllerId).get(knownKey1)).isEqualTo(knownValue1);
|
||||
assertThat(targetManagement.getMetadata(knownControllerId).get(knownKey2)).isEqualTo(knownValue2);
|
||||
|
||||
// verify quota enforcement
|
||||
final int maxMetaData = quotaManagement.getMaxMetaDataEntriesPerTarget();
|
||||
@@ -2025,16 +1998,14 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
|
||||
metaData2.put(new JSONObject().put("key", knownKey1 + i).put("value", knownValue1 + i));
|
||||
}
|
||||
|
||||
mvc.perform(post("/rest/v1/targets/{targetId}/metadata", knownControllerId).accept(MediaType.APPLICATION_JSON)
|
||||
mvc.perform(post("/rest/v1/targets/{targetId}/metadata", knownControllerId)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.contentType(MediaType.APPLICATION_JSON).content(metaData2.toString()))
|
||||
.andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isForbidden());
|
||||
|
||||
// verify that the number of meta data entries has not changed
|
||||
// (we cannot use the PAGE constant here as it tries to sort by ID)
|
||||
assertThat(targetManagement.findMetaDataByControllerId(PageRequest.of(0, Integer.MAX_VALUE), knownControllerId)
|
||||
.getTotalElements()).isEqualTo(metaData1.length());
|
||||
|
||||
// verify that the number of meta-data entries has not changed (we cannot use the PAGE constant here as it tries to sort by ID)
|
||||
assertThat(targetManagement.getMetadata(knownControllerId)).hasSize(metaData1.length());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -2056,14 +2027,9 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
|
||||
.content(jsonObject.toString()))
|
||||
.andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
|
||||
.andExpect(jsonPath("key", equalTo(knownKey)))
|
||||
.andExpect(jsonPath("value", equalTo(updateValue)));
|
||||
|
||||
final TargetMetadata updatedTargetMetadata = targetManagement
|
||||
.getMetaDataByControllerId(knownControllerId, knownKey).get();
|
||||
assertThat(updatedTargetMetadata.getValue()).isEqualTo(updateValue);
|
||||
.andExpect(content().string(""));
|
||||
|
||||
assertThat(targetManagement.getMetadata(knownControllerId).get(knownKey)).isEqualTo(updateValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -2081,7 +2047,12 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
|
||||
.andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isOk());
|
||||
|
||||
assertThat(targetManagement.getMetaDataByControllerId(knownControllerId, knownKey)).isNotPresent();
|
||||
// already deleted
|
||||
mvc.perform(delete("/rest/v1/targets/{targetId}/metadata/{key}", knownControllerId, knownKey))
|
||||
.andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
assertThat(targetManagement.getMetadata(knownControllerId).get(knownKey)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -2103,7 +2074,7 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
|
||||
.andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
assertThat(targetManagement.getMetaDataByControllerId(knownControllerId, knownKey)).isPresent();
|
||||
assertThat(targetManagement.getMetadata(knownControllerId).get(knownKey)).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -2126,53 +2097,27 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
|
||||
|
||||
@Test
|
||||
@Description("Ensures that a metadata entry paged list selection through API reflectes the repository content.")
|
||||
void getPagedListOfMetadata() throws Exception {
|
||||
void getMetadata() throws Exception {
|
||||
final String knownControllerId = "targetIdWithMetadata";
|
||||
|
||||
final int totalMetadata = 10;
|
||||
final int limitParam = 5;
|
||||
final String offsetParam = "0";
|
||||
final String knownKeyPrefix = "knownKey";
|
||||
final String knownValuePrefix = "knownValue";
|
||||
|
||||
setupTargetWithMetadata(knownControllerId, knownKeyPrefix, knownValuePrefix, totalMetadata);
|
||||
|
||||
mvc.perform(get("/rest/v1/targets/{targetId}/metadata?offset=" + offsetParam + "&limit=" + limitParam,
|
||||
knownControllerId))
|
||||
mvc.perform(get("/rest/v1/targets/{targetId}/metadata", knownControllerId))
|
||||
.andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("size", equalTo(limitParam)))
|
||||
.andExpect(jsonPath("size", equalTo(totalMetadata)))
|
||||
.andExpect(jsonPath("total", equalTo(totalMetadata)))
|
||||
.andExpect(jsonPath("content[0].key", equalTo("knownKey0")))
|
||||
.andExpect(jsonPath("content[0].value", equalTo("knownValue0")));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Ensures that a target metadata filtered query with value==knownValue1 parameter returns only the metadata entries with that value.")
|
||||
void searchDistributionSetMetadataRsql() throws Exception {
|
||||
final String knownControllerId = "targetIdWithMetadata";
|
||||
|
||||
final int totalMetadata = 10;
|
||||
final String knownKeyPrefix = "knownKey";
|
||||
final String knownValuePrefix = "knownValue";
|
||||
|
||||
setupTargetWithMetadata(knownControllerId, knownKeyPrefix, knownValuePrefix, totalMetadata);
|
||||
|
||||
final String rsqlSearchValue1 = "value==knownValue1";
|
||||
|
||||
mvc.perform(get("/rest/v1/targets/{targetId}/metadata?q=" + rsqlSearchValue1, knownControllerId))
|
||||
.andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("size", equalTo(1)))
|
||||
.andExpect(jsonPath("total", equalTo(1)))
|
||||
.andExpect(jsonPath("content[0].key", equalTo("knownKey1")))
|
||||
.andExpect(jsonPath("content[0].value", equalTo("knownValue1")));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("A request for assigning multiple DS to a target results in a Bad Request when multiassignment in disabled.")
|
||||
void multiassignmentRequestNotAllowedIfDisabled() throws Exception {
|
||||
void multiAssignmentRequestNotAllowedIfDisabled() throws Exception {
|
||||
final String targetId = testdataFactory.createTarget().getControllerId();
|
||||
final List<Long> dsIds = testdataFactory.createDistributionSets(2).stream().map(DistributionSet::getId)
|
||||
.toList();
|
||||
@@ -2953,23 +2898,20 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
|
||||
return targetManagement.getByControllerID(tA.getControllerId()).get();
|
||||
}
|
||||
|
||||
private void setupTargetWithMetadata(final String knownControllerId, final String knownKey,
|
||||
final String knownValue) {
|
||||
private void setupTargetWithMetadata(final String knownControllerId, final String knownKey, final String knownValue) {
|
||||
testdataFactory.createTarget(knownControllerId);
|
||||
targetManagement.createMetaData(knownControllerId,
|
||||
Collections.singletonList(entityFactory.generateTargetMetadata(knownKey, knownValue)));
|
||||
targetManagement.createMetadata(knownControllerId, Map.of(knownKey, knownValue));
|
||||
}
|
||||
|
||||
private void setupTargetWithMetadata(final String knownControllerId, final String knownKeyPrefix,
|
||||
final String knownValuePrefix, final int totalMetadata) {
|
||||
private void setupTargetWithMetadata(
|
||||
final String knownControllerId, final String knownKeyPrefix, final String knownValuePrefix, final int totalMetadata) {
|
||||
testdataFactory.createTarget(knownControllerId);
|
||||
|
||||
final List<MetaData> targetMetadataEntries = new LinkedList<>();
|
||||
final Map<String, String> metadataEntries = new HashMap<>();
|
||||
for (int index = 0; index < totalMetadata; index++) {
|
||||
targetMetadataEntries
|
||||
.add(entityFactory.generateTargetMetadata(knownKeyPrefix + index, knownValuePrefix + index));
|
||||
metadataEntries.put(knownKeyPrefix + index, knownValuePrefix + index);
|
||||
}
|
||||
targetManagement.createMetaData(knownControllerId, targetMetadataEntries);
|
||||
targetManagement.createMetadata(knownControllerId, metadataEntries);
|
||||
}
|
||||
|
||||
private Action updateActionStatus(final Action action, final Status status, final Integer statusCode) {
|
||||
|
||||
@@ -54,16 +54,6 @@ public interface EntityFactory {
|
||||
MetaData generateDsMetadata(@Size(min = 1, max = MetaData.KEY_MAX_SIZE) @NotNull String key,
|
||||
@Size(max = MetaData.VALUE_MAX_SIZE) String value);
|
||||
|
||||
/**
|
||||
* Generates an {@link MetaData} element for target without persisting it.
|
||||
*
|
||||
* @param key {@link MetaData#getKey()}
|
||||
* @param value {@link MetaData#getValue()}
|
||||
* @return {@link MetaData} object
|
||||
*/
|
||||
MetaData generateTargetMetadata(@Size(min = 1, max = MetaData.KEY_MAX_SIZE) @NotNull String key,
|
||||
@Size(max = MetaData.VALUE_MAX_SIZE) String value);
|
||||
|
||||
/**
|
||||
* @return {@link SoftwareModuleMetadataBuilder} object
|
||||
*/
|
||||
|
||||
@@ -35,7 +35,6 @@ import org.eclipse.hawkbit.repository.model.MetaData;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||
import org.eclipse.hawkbit.repository.model.TargetMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.TargetTag;
|
||||
import org.eclipse.hawkbit.repository.model.TargetType;
|
||||
import org.eclipse.hawkbit.repository.model.TargetTypeAssignmentResult;
|
||||
@@ -434,8 +433,7 @@ public interface TargetManagement {
|
||||
* @throws EntityNotFoundException if distribution set with given ID does not exist
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_READ_TARGET)
|
||||
Page<Target> findByInstalledDistributionSetAndRsql(@NotNull Pageable pageReq, long distributionSetId,
|
||||
@NotNull String rsqlParam);
|
||||
Page<Target> findByInstalledDistributionSetAndRsql(@NotNull Pageable pageReq, long distributionSetId, @NotNull String rsqlParam);
|
||||
|
||||
/**
|
||||
* Retrieves the {@link Target} which have a certain {@link TargetUpdateStatus}.
|
||||
@@ -509,6 +507,19 @@ public interface TargetManagement {
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
||||
Page<Target> findByRsqlAndTag(@NotNull Pageable pageable, @NotNull String rsqlParam, long tagId);
|
||||
|
||||
/**
|
||||
* Verify if a target matches a specific target filter query, does not have a
|
||||
* specific DS already assigned and is compatible with it.
|
||||
*
|
||||
* @param controllerId of the {@link org.eclipse.hawkbit.repository.model.Target} to check
|
||||
* @param distributionSetId of the {@link org.eclipse.hawkbit.repository.model.DistributionSet} to consider
|
||||
* @param targetFilterQuery to execute
|
||||
* @return true if it matches
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_READ_TARGET)
|
||||
boolean isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(
|
||||
@NotNull String controllerId, long distributionSetId, @NotNull String targetFilterQuery);
|
||||
|
||||
/**
|
||||
* Initiates {@link TargetType} assignment to given {@link Target}s. If some
|
||||
* targets in the list have the {@link TargetType} not yet assigned, they will
|
||||
@@ -525,12 +536,10 @@ public interface TargetManagement {
|
||||
TargetTypeAssignmentResult assignType(@NotEmpty Collection<String> controllerIds, @NotNull Long typeId);
|
||||
|
||||
/**
|
||||
* Initiates {@link TargetType} un-assignment to given {@link Target}s. The type
|
||||
* of the targets will be set to {@code null}
|
||||
* Initiates {@link TargetType} un-assignment to given {@link Target}s. The type of the targets will be set to {@code null}
|
||||
*
|
||||
* @param controllerIds to remove the type from
|
||||
* @return {@link TargetTypeAssignmentResult} with all metadata of the
|
||||
* assignment outcome.
|
||||
* @return {@link TargetTypeAssignmentResult} with all metadata of the assignment outcome.
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
|
||||
TargetTypeAssignmentResult unassignType(@NotEmpty Collection<String> controllerIds);
|
||||
@@ -631,6 +640,25 @@ public interface TargetManagement {
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
||||
List<Target> get(@NotNull Collection<Long> ids);
|
||||
|
||||
/**
|
||||
* Verifies that {@link Target} with given controller ID exists in the repository.
|
||||
*
|
||||
* @param controllerId of target
|
||||
* @return {@code true} if target with given ID exists
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
||||
boolean existsByControllerId(@NotEmpty String controllerId);
|
||||
|
||||
/**
|
||||
* Finds a single target tags its id.
|
||||
*
|
||||
* @param controllerId of the {@link Target}
|
||||
* @return the found Tag set
|
||||
* @throws EntityNotFoundException if target with given ID does not exist
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
||||
Set<TargetTag> getTags(@NotEmpty String controllerId);
|
||||
|
||||
/**
|
||||
* Get controller attributes of given {@link Target}.
|
||||
*
|
||||
@@ -671,55 +699,37 @@ public interface TargetManagement {
|
||||
Page<Target> findByControllerAttributesRequested(@NotNull Pageable pageReq);
|
||||
|
||||
/**
|
||||
* Verifies that {@link Target} with given controller ID exists in the
|
||||
* repository.
|
||||
*
|
||||
* @param controllerId of target
|
||||
* @return {@code true} if target with given ID exists
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
||||
boolean existsByControllerId(@NotEmpty String controllerId);
|
||||
|
||||
/**
|
||||
* Verify if a target matches a specific target filter query, does not have a
|
||||
* specific DS already assigned and is compatible with it.
|
||||
*
|
||||
* @param controllerId of the {@link org.eclipse.hawkbit.repository.model.Target} to
|
||||
* check
|
||||
* @param distributionSetId of the
|
||||
* {@link org.eclipse.hawkbit.repository.model.DistributionSet} to
|
||||
* consider
|
||||
* @param targetFilterQuery to execute
|
||||
* @return true if it matches
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_READ_TARGET)
|
||||
boolean isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(@NotNull String controllerId,
|
||||
long distributionSetId, @NotNull String targetFilterQuery);
|
||||
|
||||
/**
|
||||
* Finds a single target tags its id.
|
||||
*
|
||||
* @param controllerId of the {@link Target}
|
||||
* @return the found Tag set
|
||||
* @throws EntityNotFoundException if target with given ID does not exist
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
||||
Set<TargetTag> getTagsByControllerId(@NotEmpty String controllerId);
|
||||
|
||||
/**
|
||||
* Creates a list of target meta data entries.
|
||||
* Creates a list of target meta-data entries.
|
||||
*
|
||||
* @param controllerId {@link Target} controller id the metadata has to be created for
|
||||
* @param metadata the meta data entries to create or update
|
||||
* @return the updated or created target metadata entries
|
||||
* @param metadata the meta-data entries to create or update
|
||||
* @throws EntityNotFoundException if given target does not exist
|
||||
* @throws EntityAlreadyExistsException in case one of the metadata entry already exists for the specific
|
||||
* key
|
||||
* @throws AssignmentQuotaExceededException if the maximum number of {@link MetaData} entries is exceeded for
|
||||
* the addressed {@link Target}
|
||||
* @throws EntityAlreadyExistsException in case one of the metadata entry already exists for the specific key
|
||||
* @throws AssignmentQuotaExceededException if the maximum number of {@link MetaData} entries is exceeded for the addressed {@link Target}
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
|
||||
List<TargetMetadata> createMetaData(@NotEmpty String controllerId, @NotEmpty Collection<MetaData> metadata);
|
||||
void createMetadata(@NotEmpty String controllerId, @NotEmpty Map<String, String> metadata);
|
||||
|
||||
/**
|
||||
* Finds a single target meta-data by its id.
|
||||
*
|
||||
* @param controllerId of the {@link Target}
|
||||
* @return the found target metadata
|
||||
* @throws EntityNotFoundException if target with given ID does not exist
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
|
||||
Map<String, String> getMetadata(@NotEmpty String controllerId);
|
||||
|
||||
/**
|
||||
* Updates a target meta-data value if corresponding entry exists.
|
||||
*
|
||||
* @param controllerId {@link Target} controller id of the metadata entry to be updated
|
||||
* @param key meta data-entry key to be updated
|
||||
* @param value meta data-entry to be new value
|
||||
* @throws EntityNotFoundException in case the metadata entry does not exist and cannot be updated
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
|
||||
void updateMetadata(@NotEmpty String controllerId, @NotNull String key, @NotNull String value);
|
||||
|
||||
/**
|
||||
* Deletes a target meta data entry.
|
||||
@@ -729,64 +739,5 @@ public interface TargetManagement {
|
||||
* @throws EntityNotFoundException if given target does not exist
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
|
||||
void deleteMetaData(@NotEmpty String controllerId, @NotEmpty String key);
|
||||
|
||||
/**
|
||||
* Finds all meta data by the given target id.
|
||||
*
|
||||
* @param pageable the page request to page the result
|
||||
* @param controllerId the controller id to retrieve the metadata from
|
||||
* @return a paged result of all meta data entries for a given target id
|
||||
* @throws EntityNotFoundException if target with given ID does not exist
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
|
||||
Page<TargetMetadata> findMetaDataByControllerId(@NotNull Pageable pageable, @NotEmpty String controllerId);
|
||||
|
||||
/**
|
||||
* Counts all meta data by the given target id.
|
||||
*
|
||||
* @param controllerId the controller id to retrieve the meta data from
|
||||
* @return count of all meta data entries for a given target id
|
||||
* @throws EntityNotFoundException if target with given ID does not exist
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
|
||||
long countMetaDataByControllerId(@NotEmpty String controllerId);
|
||||
|
||||
/**
|
||||
* Finds all metadata by the given target id and query.
|
||||
*
|
||||
* @param pageable the page request to page the result
|
||||
* @param controllerId the controller id to retrieve the metadata from
|
||||
* @param rsqlParam rsql query string
|
||||
* @return a paged result of all meta data entries for a given target id
|
||||
* @throws RSQLParameterUnsupportedFieldException if a field in the RSQL string is used but not provided by the
|
||||
* given {@code fieldNameProvider}
|
||||
* @throws RSQLParameterSyntaxException if the RSQL syntax is wrong
|
||||
* @throws EntityNotFoundException if target with given ID does not exist
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
|
||||
Page<TargetMetadata> findMetaDataByControllerIdAndRsql(@NotNull Pageable pageable, @NotEmpty String controllerId,
|
||||
@NotNull String rsqlParam);
|
||||
|
||||
/**
|
||||
* Finds a single target meta data by its id.
|
||||
*
|
||||
* @param controllerId of the {@link Target}
|
||||
* @param key of the meta data element
|
||||
* @return the found TargetMetadata
|
||||
* @throws EntityNotFoundException if target with given ID does not exist
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
|
||||
Optional<TargetMetadata> getMetaDataByControllerId(@NotEmpty String controllerId, @NotEmpty String key);
|
||||
|
||||
/**
|
||||
* Updates a target meta data value if corresponding entry exists.
|
||||
*
|
||||
* @param controllerId {@link Target} controller id of the metadata entry to be updated
|
||||
* @param metadata meta data entry to be updated
|
||||
* @return the updated meta data entry
|
||||
* @throws EntityNotFoundException in case the metadata entry does not exist and cannot be updated
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
|
||||
TargetMetadata updateMetadata(@NotEmpty String controllerId, @NotNull MetaData metadata);
|
||||
void deleteMetadata(@NotEmpty String controllerId, @NotEmpty String key);
|
||||
}
|
||||
@@ -75,10 +75,20 @@ public class EntityNotFoundException extends AbstractServerRtException {
|
||||
* @param type of the entity that was not found
|
||||
* @param entityId of the {@link BaseEntity}
|
||||
*/
|
||||
public EntityNotFoundException(final Class<? extends BaseEntity> type, final Object entityId) {
|
||||
super(type.getSimpleName() + " with given identifier {" + entityId + "} does not exist.",
|
||||
public EntityNotFoundException(final String type, final Object entityId) {
|
||||
super(type + " with given identifier {" + entityId + "} does not exist.",
|
||||
THIS_ERROR,
|
||||
Map.of(TYPE, type.getSimpleName(), ENTITY_ID, entityId));
|
||||
Map.of(TYPE, type, ENTITY_ID, entityId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameterized constructor for {@link BaseEntity} not found.
|
||||
*
|
||||
* @param type of the entity that was not found
|
||||
* @param entityId of the {@link BaseEntity}
|
||||
*/
|
||||
public EntityNotFoundException(final Class<? extends BaseEntity> type, final Object entityId) {
|
||||
this(type.getSimpleName(), entityId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -47,6 +47,16 @@ public interface Target extends NamedEntity {
|
||||
*/
|
||||
int CONTROLLER_ATTRIBUTE_VALUE_SIZE = 128;
|
||||
|
||||
/**
|
||||
* Maximum length of metadata key.
|
||||
*/
|
||||
int CONTROLLER_METADATA_KEY_SIZE = 128;
|
||||
|
||||
/**
|
||||
* Maximum length of metadata value.
|
||||
*/
|
||||
int CONTROLLER_METADATA_VALUE_SIZE = 4000;
|
||||
|
||||
/**
|
||||
* @return business identifier of the {@link Target}
|
||||
*/
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2018 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.model;
|
||||
|
||||
/**
|
||||
* {@link MetaData} of a {@link Target}.
|
||||
*/
|
||||
public interface TargetMetadata extends MetaData {
|
||||
|
||||
/**
|
||||
* @return {@link Target} of this {@link MetaData} entry.
|
||||
*/
|
||||
Target getTarget();
|
||||
|
||||
@Override
|
||||
default Long getEntityId() {
|
||||
return getTarget().getId();
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,6 @@ import org.eclipse.hawkbit.repository.jpa.builder.JpaRolloutGroupBuilder;
|
||||
import org.eclipse.hawkbit.repository.jpa.builder.JpaSoftwareModuleTypeBuilder;
|
||||
import org.eclipse.hawkbit.repository.jpa.builder.JpaTagBuilder;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetMetadata;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.MetaData;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
@@ -77,11 +76,6 @@ public class JpaEntityFactory implements EntityFactory {
|
||||
return new JpaDistributionSetMetadata(key, value == null ? null : value.strip());
|
||||
}
|
||||
|
||||
@Override
|
||||
public MetaData generateTargetMetadata(final String key, final String value) {
|
||||
return new JpaTargetMetadata(key, value == null ? null : value.strip());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SoftwareModuleMetadataBuilder softwareModuleMetadata() {
|
||||
return softwareModuleMetadataBuilder;
|
||||
|
||||
@@ -132,7 +132,6 @@ import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleMetadataRepos
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleTypeRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.TargetFilterQueryRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.TargetMetadataRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.TargetTagRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.TargetTypeRepository;
|
||||
@@ -622,7 +621,7 @@ public class RepositoryApplicationConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
TargetManagement targetManagement(final EntityManager entityManager, final QuotaManagement quotaManagement,
|
||||
final TargetRepository targetRepository, final TargetMetadataRepository targetMetadataRepository,
|
||||
final TargetRepository targetRepository,
|
||||
final RolloutGroupRepository rolloutGroupRepository,
|
||||
final TargetFilterQueryRepository targetFilterQueryRepository,
|
||||
final TargetTypeRepository targetTypeRepository, final TargetTagRepository targetTagRepository,
|
||||
@@ -630,7 +629,7 @@ public class RepositoryApplicationConfiguration {
|
||||
final VirtualPropertyReplacer virtualPropertyReplacer,
|
||||
final JpaProperties properties, final DistributionSetManagement distributionSetManagement) {
|
||||
return new JpaTargetManagement(entityManager, distributionSetManagement, quotaManagement, targetRepository,
|
||||
targetTypeRepository, targetMetadataRepository, rolloutGroupRepository, targetFilterQueryRepository,
|
||||
targetTypeRepository, rolloutGroupRepository, targetFilterQueryRepository,
|
||||
targetTagRepository, eventPublisherHolder, tenantAware, virtualPropertyReplacer,
|
||||
properties.getDatabase());
|
||||
}
|
||||
|
||||
@@ -37,12 +37,7 @@ public class JpaTargetCreate extends AbstractTargetUpdateCreate<TargetCreate> im
|
||||
|
||||
@Override
|
||||
public JpaTarget build() {
|
||||
final JpaTarget target;
|
||||
if (ObjectUtils.isEmpty(securityToken)) {
|
||||
target = new JpaTarget(controllerId);
|
||||
} else {
|
||||
target = new JpaTarget(controllerId, securityToken);
|
||||
}
|
||||
final JpaTarget target = new JpaTarget(controllerId, securityToken);
|
||||
|
||||
if (!ObjectUtils.isEmpty(name)) {
|
||||
target.setName(name);
|
||||
|
||||
@@ -31,6 +31,7 @@ import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
import jakarta.persistence.criteria.CriteriaQuery;
|
||||
import jakarta.persistence.criteria.MapJoin;
|
||||
import jakarta.persistence.criteria.Root;
|
||||
import jakarta.persistence.metamodel.MapAttribute;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
|
||||
import org.apache.commons.collections4.ListUtils;
|
||||
@@ -39,12 +40,10 @@ import org.eclipse.hawkbit.repository.FilterParams;
|
||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetFields;
|
||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetMetadataFields;
|
||||
import org.eclipse.hawkbit.repository.TimestampCalculator;
|
||||
import org.eclipse.hawkbit.repository.builder.TargetCreate;
|
||||
import org.eclipse.hawkbit.repository.builder.TargetUpdate;
|
||||
import org.eclipse.hawkbit.repository.event.remote.TargetAttributesRequestedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
|
||||
@@ -52,18 +51,13 @@ import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
|
||||
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetCreate;
|
||||
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetUpdate;
|
||||
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetMetadata;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetMetadata_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.TargetMetadataCompositeKey;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.helper.AfterTransactionCommitExecutorHolder;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.RolloutGroupRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.TargetFilterQueryRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.TargetMetadataRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.TargetTagRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.TargetTypeRepository;
|
||||
@@ -73,11 +67,9 @@ import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications;
|
||||
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.model.MetaData;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||
import org.eclipse.hawkbit.repository.model.TargetMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.TargetTag;
|
||||
import org.eclipse.hawkbit.repository.model.TargetType;
|
||||
import org.eclipse.hawkbit.repository.model.TargetTypeAssignmentResult;
|
||||
@@ -109,7 +101,6 @@ public class JpaTargetManagement implements TargetManagement {
|
||||
private final QuotaManagement quotaManagement;
|
||||
private final TargetRepository targetRepository;
|
||||
private final TargetTypeRepository targetTypeRepository;
|
||||
private final TargetMetadataRepository targetMetadataRepository;
|
||||
private final RolloutGroupRepository rolloutGroupRepository;
|
||||
private final TargetFilterQueryRepository targetFilterQueryRepository;
|
||||
private final TargetTagRepository targetTagRepository;
|
||||
@@ -122,7 +113,6 @@ public class JpaTargetManagement implements TargetManagement {
|
||||
public JpaTargetManagement(final EntityManager entityManager,
|
||||
final DistributionSetManagement distributionSetManagement, final QuotaManagement quotaManagement,
|
||||
final TargetRepository targetRepository, final TargetTypeRepository targetTypeRepository,
|
||||
final TargetMetadataRepository targetMetadataRepository,
|
||||
final RolloutGroupRepository rolloutGroupRepository,
|
||||
final TargetFilterQueryRepository targetFilterQueryRepository,
|
||||
final TargetTagRepository targetTagRepository, final EventPublisherHolder eventPublisherHolder,
|
||||
@@ -133,7 +123,6 @@ public class JpaTargetManagement implements TargetManagement {
|
||||
this.quotaManagement = quotaManagement;
|
||||
this.targetRepository = targetRepository;
|
||||
this.targetTypeRepository = targetTypeRepository;
|
||||
this.targetMetadataRepository = targetMetadataRepository;
|
||||
this.rolloutGroupRepository = rolloutGroupRepository;
|
||||
this.targetFilterQueryRepository = targetFilterQueryRepository;
|
||||
this.targetTagRepository = targetTagRepository;
|
||||
@@ -635,57 +624,6 @@ public class JpaTargetManagement implements TargetManagement {
|
||||
return Collections.unmodifiableList(targetRepository.findAll(TargetSpecifications.hasIdIn(ids)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getControllerAttributes(final String controllerId) {
|
||||
getByControllerIdAndThrowIfNotFound(controllerId);
|
||||
|
||||
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
||||
final CriteriaQuery<Object[]> query = cb.createQuery(Object[].class);
|
||||
|
||||
final Root<JpaTarget> targetRoot = query.from(JpaTarget.class);
|
||||
query.where(cb.equal(targetRoot.get(JpaTarget_.controllerId), controllerId));
|
||||
|
||||
final MapJoin<JpaTarget, String, String> attributes = targetRoot.join(JpaTarget_.controllerAttributes);
|
||||
query.multiselect(attributes.key(), attributes.value());
|
||||
query.orderBy(cb.asc(attributes.key()));
|
||||
|
||||
final List<Object[]> attr = entityManager.createQuery(query).getResultList();
|
||||
|
||||
return attr.stream().collect(Collectors.toMap(entry -> (String) entry[0], entry -> (String) entry[1],
|
||||
(v1, v2) -> v1, LinkedHashMap::new));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public void requestControllerAttributes(final String controllerId) {
|
||||
final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId);
|
||||
targetRepository.getAccessController()
|
||||
.ifPresent(acm -> acm.assertOperationAllowed(AccessController.Operation.UPDATE, target));
|
||||
target.setRequestControllerAttributes(true);
|
||||
AfterTransactionCommitExecutorHolder.getInstance().getAfterCommit().afterCommit(() ->
|
||||
eventPublisherHolder.getEventPublisher()
|
||||
.publishEvent(new TargetAttributesRequestedEvent(
|
||||
tenantAware.getCurrentTenant(), target.getId(), target.getControllerId(),
|
||||
target.getAddress() != null ? target.getAddress().toString() : null,
|
||||
JpaTarget.class, eventPublisherHolder.getApplicationId())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isControllerAttributesRequested(final String controllerId) {
|
||||
final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId);
|
||||
|
||||
return target.isRequestControllerAttributes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Target> findByControllerAttributesRequested(final Pageable pageReq) {
|
||||
return JpaManagementHelper.findAllWithCountBySpec(targetRepository, List.of(TargetSpecifications.hasRequestControllerAttributesTrue()),
|
||||
pageReq
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean existsByControllerId(final String controllerId) {
|
||||
return targetRepository.exists(TargetSpecifications.hasControllerId(controllerId));
|
||||
@@ -710,115 +648,120 @@ public class JpaTargetManagement implements TargetManagement {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<TargetTag> getTagsByControllerId(@NotEmpty String controllerId) {
|
||||
public Set<TargetTag> getTags(@NotEmpty String controllerId) {
|
||||
// the method has PreAuthorized by itself
|
||||
return ((JpaTarget)getWithTags(controllerId)).getTags();
|
||||
return ((JpaTarget) getWithTags(controllerId)).getTags();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getControllerAttributes(final String controllerId) {
|
||||
return getMap(controllerId, JpaTarget_.controllerAttributes);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public List<TargetMetadata> createMetaData(final String controllerId, final Collection<MetaData> md) {
|
||||
public void requestControllerAttributes(final String controllerId) {
|
||||
final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId);
|
||||
|
||||
md.forEach(meta -> checkAndThrowIfTargetMetadataAlreadyExists(
|
||||
new TargetMetadataCompositeKey(target.getId(), meta.getKey())));
|
||||
|
||||
assertMetaDataQuota(target.getId(), md.size());
|
||||
|
||||
final JpaTarget updatedTarget = JpaManagementHelper.touch(entityManager, targetRepository, target);
|
||||
|
||||
final List<TargetMetadata> createdMetadata = md.stream()
|
||||
.map(meta -> targetMetadataRepository.save(new JpaTargetMetadata(meta.getKey(), meta.getValue(), updatedTarget)))
|
||||
.collect(Collectors.toUnmodifiableList());
|
||||
|
||||
// TargetUpdatedEvent is not sent within the touch() method due to the
|
||||
// "lastModifiedAt" field being ignored in JpaTarget
|
||||
eventPublisherHolder.getEventPublisher().publishEvent(new TargetUpdatedEvent(updatedTarget, eventPublisherHolder.getApplicationId()));
|
||||
|
||||
return createdMetadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public void deleteMetaData(final String controllerId, final String key) {
|
||||
final JpaTargetMetadata metadata = (JpaTargetMetadata) getMetaDataByControllerId(controllerId, key)
|
||||
.orElseThrow(() -> new EntityNotFoundException(TargetMetadata.class, controllerId, key));
|
||||
|
||||
final JpaTarget target = JpaManagementHelper.touch(
|
||||
entityManager, targetRepository, getByControllerIdAndThrowIfNotFound(controllerId));
|
||||
|
||||
targetRepository.getAccessController()
|
||||
.ifPresent(acm -> acm.assertOperationAllowed(AccessController.Operation.UPDATE, target));
|
||||
|
||||
targetMetadataRepository.deleteById(metadata.getId());
|
||||
// target update event is set to ignore "lastModifiedAt" field, so it is
|
||||
// not send automatically within the touch() method
|
||||
target.setRequestControllerAttributes(true);
|
||||
AfterTransactionCommitExecutorHolder.getInstance().getAfterCommit().afterCommit(() ->
|
||||
eventPublisherHolder.getEventPublisher()
|
||||
.publishEvent(new TargetUpdatedEvent(target, eventPublisherHolder.getApplicationId()));
|
||||
.publishEvent(new TargetAttributesRequestedEvent(
|
||||
tenantAware.getCurrentTenant(), target.getId(), target.getControllerId(),
|
||||
target.getAddress() != null ? target.getAddress().toString() : null,
|
||||
JpaTarget.class, eventPublisherHolder.getApplicationId())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<TargetMetadata> findMetaDataByControllerId(final Pageable pageable, final String controllerId) {
|
||||
final Long id = getByControllerIdAndThrowIfNotFound(controllerId).getId();
|
||||
|
||||
return JpaManagementHelper.findAllWithCountBySpec(targetMetadataRepository, Collections.singletonList(metadataByTargetIdSpec(id)),
|
||||
pageable
|
||||
);
|
||||
public boolean isControllerAttributesRequested(final String controllerId) {
|
||||
return getByControllerIdAndThrowIfNotFound(controllerId).isRequestControllerAttributes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countMetaDataByControllerId(@NotEmpty final String controllerId) {
|
||||
final Long targetId = getByControllerIdAndThrowIfNotFound(controllerId).getId();
|
||||
|
||||
return JpaManagementHelper.countBySpec(targetMetadataRepository,
|
||||
Collections.singletonList(metadataByTargetIdSpec(targetId)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<TargetMetadata> findMetaDataByControllerIdAndRsql(final Pageable pageable, final String controllerId,
|
||||
final String rsqlParam) {
|
||||
final Long targetId = getByControllerIdAndThrowIfNotFound(controllerId).getId();
|
||||
|
||||
final List<Specification<JpaTargetMetadata>> specList = Arrays.asList(RSQLUtility
|
||||
.buildRsqlSpecification(rsqlParam, TargetMetadataFields.class, virtualPropertyReplacer, database),
|
||||
metadataByTargetIdSpec(targetId));
|
||||
|
||||
return JpaManagementHelper.findAllWithCountBySpec(targetMetadataRepository, specList, pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<TargetMetadata> getMetaDataByControllerId(final String controllerId, final String key) {
|
||||
final Long targetId = getByControllerIdAndThrowIfNotFound(controllerId).getId();
|
||||
|
||||
return targetMetadataRepository.findById(new TargetMetadataCompositeKey(targetId, key)).map(t -> t);
|
||||
public Page<Target> findByControllerAttributesRequested(final Pageable pageReq) {
|
||||
return JpaManagementHelper.findAllWithCountBySpec(
|
||||
targetRepository, List.of(TargetSpecifications.hasRequestControllerAttributesTrue()), pageReq);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public TargetMetadata updateMetadata(final String controllerId, final MetaData md) {
|
||||
public void createMetadata(final String controllerId, final Map<String, String> md) {
|
||||
final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId);
|
||||
|
||||
// check if exists otherwise throw entity not found exception
|
||||
final JpaTargetMetadata updatedMetadata = (JpaTargetMetadata) getMetaDataByControllerId(controllerId,
|
||||
md.getKey())
|
||||
.orElseThrow(() -> new EntityNotFoundException(TargetMetadata.class, controllerId, md.getKey()));
|
||||
updatedMetadata.setValue(md.getValue());
|
||||
// touch it to update the lock revision because we are modifying the
|
||||
// target indirectly
|
||||
final JpaTarget target = JpaManagementHelper.touch(entityManager, targetRepository,
|
||||
getByControllerIdAndThrowIfNotFound(controllerId));
|
||||
// get the modifiable metadata map
|
||||
final Map<String, String> metadata = target.getMetadata();
|
||||
md.keySet().forEach(key -> {
|
||||
if (metadata.containsKey(key)) {
|
||||
throw new EntityAlreadyExistsException("Metadata entry with key '" + key + "' already exists");
|
||||
}
|
||||
});
|
||||
metadata.putAll(md);
|
||||
|
||||
final JpaTargetMetadata metadata = targetMetadataRepository.save(updatedMetadata);
|
||||
// target update event is set to ignore "lastModifiedAt" field, so it is
|
||||
// not send automatically within the touch() method
|
||||
eventPublisherHolder.getEventPublisher()
|
||||
.publishEvent(new TargetUpdatedEvent(target, eventPublisherHolder.getApplicationId()));
|
||||
return metadata;
|
||||
assertMetadataQuota(target.getId(), metadata.size());
|
||||
targetRepository.save(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getMetadata(final String controllerId) {
|
||||
return getMap(controllerId, JpaTarget_.metadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public void updateMetadata(final String controllerId, final String key, final String value) {
|
||||
final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId);
|
||||
|
||||
// get the modifiable metadata map
|
||||
final Map<String, String> metadata = target.getMetadata();
|
||||
if (!metadata.containsKey(key)) {
|
||||
throw new EntityNotFoundException("Target metadata", controllerId + ":" + key);
|
||||
}
|
||||
metadata.put(key, value);
|
||||
|
||||
targetRepository.save(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public void deleteMetadata(final String controllerId, final String key) {
|
||||
final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId);
|
||||
|
||||
// get the modifiable metadata map
|
||||
final Map<String, String> metadata = target.getMetadata();
|
||||
if (metadata.remove(key) == null) {
|
||||
throw new EntityNotFoundException("Target metadata", controllerId + ":" + key);
|
||||
}
|
||||
|
||||
targetRepository.save(target);
|
||||
}
|
||||
|
||||
private Map<String, String> getMap(final String controllerId, final MapAttribute<JpaTarget, String, String> mapAttribute) {
|
||||
getByControllerIdAndThrowIfNotFound(controllerId);
|
||||
|
||||
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
||||
final CriteriaQuery<Object[]> query = cb.createQuery(Object[].class);
|
||||
|
||||
final Root<JpaTarget> targetRoot = query.from(JpaTarget.class);
|
||||
query.where(cb.equal(targetRoot.get(JpaTarget_.controllerId), controllerId));
|
||||
|
||||
final MapJoin<JpaTarget, String, String> mapJoin = targetRoot.join(mapAttribute);
|
||||
query.multiselect(mapJoin.key(), mapJoin.value());
|
||||
query.orderBy(cb.asc(mapJoin.key()));
|
||||
|
||||
return entityManager
|
||||
.createQuery(query)
|
||||
.getResultList()
|
||||
.stream()
|
||||
.collect(Collectors.toMap(entry -> (String) entry[0], entry -> (String) entry[1], (v1, v2) -> v1, LinkedHashMap::new));
|
||||
}
|
||||
|
||||
private static boolean hasTagsFilterActive(final FilterParams filterParams) {
|
||||
@@ -851,20 +794,9 @@ public class JpaTargetManagement implements TargetManagement {
|
||||
return targetTypeRepository.findById(id).orElseThrow(() -> new EntityNotFoundException(TargetType.class, id));
|
||||
}
|
||||
|
||||
private void checkAndThrowIfTargetMetadataAlreadyExists(final TargetMetadataCompositeKey metadataId) {
|
||||
if (targetMetadataRepository.existsById(metadataId)) {
|
||||
throw new EntityAlreadyExistsException(
|
||||
"Metadata entry with key '" + metadataId.getKey() + "' already exists");
|
||||
}
|
||||
}
|
||||
|
||||
private void assertMetaDataQuota(final Long targetId, final int requested) {
|
||||
QuotaHelper.assertAssignmentQuota(targetId, requested, quotaManagement.getMaxMetaDataEntriesPerTarget(),
|
||||
TargetMetadata.class, Target.class, targetMetadataRepository::countByTargetId);
|
||||
}
|
||||
|
||||
private Specification<JpaTargetMetadata> metadataByTargetIdSpec(final Long targetId) {
|
||||
return (root, query, cb) -> cb.equal(root.get(JpaTargetMetadata_.target).get(AbstractJpaBaseEntity_.id), targetId);
|
||||
private void assertMetadataQuota(final Long targetId, final int requested) {
|
||||
final int limit = quotaManagement.getMaxMetaDataEntriesPerTarget();
|
||||
QuotaHelper.assertAssignmentQuota(targetId, requested, limit, "Metadata", Target.class.getSimpleName(), null);
|
||||
}
|
||||
|
||||
private List<Specification<JpaTarget>> buildSpecificationList(final FilterParams filterParams) {
|
||||
|
||||
@@ -59,7 +59,6 @@ import org.eclipse.hawkbit.repository.jpa.utils.MapAttributeConverter;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.PollStatus;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.TargetTag;
|
||||
import org.eclipse.hawkbit.repository.model.TargetType;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
@@ -181,7 +180,7 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
|
||||
})
|
||||
private Set<TargetTag> tags;
|
||||
|
||||
// no cascade option on an ElementCollection, the target objects are always persisted, merged, removed with their parent.
|
||||
// no cascade option on an ElementCollection, the target objects are always persisted, merged, removed with their parent
|
||||
@Getter
|
||||
@ElementCollection
|
||||
@CollectionTable(
|
||||
@@ -192,8 +191,16 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
|
||||
@Column(name = "attribute_value", length = Target.CONTROLLER_ATTRIBUTE_VALUE_SIZE)
|
||||
private Map<String, String> controllerAttributes;
|
||||
|
||||
@OneToMany(mappedBy = "target", fetch = FetchType.LAZY, cascade = { CascadeType.REMOVE }, targetEntity = JpaTargetMetadata.class)
|
||||
private List<TargetMetadata> metadata;
|
||||
// no cascade option on an ElementCollection, the target objects are always persisted, merged, removed with their parent
|
||||
@Getter
|
||||
@ElementCollection
|
||||
@CollectionTable(
|
||||
name = "sp_target_metadata",
|
||||
joinColumns = { @JoinColumn(name = "target", nullable = false) },
|
||||
foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_target_metadata_target"))
|
||||
@MapKeyColumn(name = "meta_key", length = Target.CONTROLLER_METADATA_KEY_SIZE)
|
||||
@Column(name = "meta_value", length = Target.CONTROLLER_METADATA_VALUE_SIZE)
|
||||
private Map<String, String> metadata;
|
||||
|
||||
@OneToMany(mappedBy = "target", fetch = FetchType.LAZY, cascade = { CascadeType.REMOVE }, targetEntity = JpaAction.class)
|
||||
private List<JpaAction> actions;
|
||||
@@ -201,15 +208,11 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
|
||||
@OneToMany(mappedBy = "target", fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.REMOVE })
|
||||
private List<RolloutTargetGroup> rolloutTargetGroup;
|
||||
|
||||
public JpaTarget(final String controllerId) {
|
||||
this(controllerId, SecurityTokenGeneratorHolder.getInstance().generateToken());
|
||||
}
|
||||
|
||||
public JpaTarget(final String controllerId, final String securityToken) {
|
||||
this.controllerId = controllerId;
|
||||
// truncate controller ID to max name length (if needed)
|
||||
// truncate controller ID to max name length (if too big)
|
||||
setName(controllerId != null && controllerId.length() > NAME_MAX_SIZE ? controllerId.substring(0, NAME_MAX_SIZE) : controllerId);
|
||||
this.securityToken = securityToken;
|
||||
this.securityToken = ObjectUtils.isEmpty(securityToken) ? SecurityTokenGeneratorHolder.getInstance().generateToken() : securityToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -287,10 +290,6 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
|
||||
return tags == null ? Collections.emptySet() : Collections.unmodifiableSet(tags);
|
||||
}
|
||||
|
||||
public List<TargetMetadata> getMetadata() {
|
||||
return metadata == null ? Collections.emptyList() : Collections.unmodifiableList(metadata);
|
||||
}
|
||||
|
||||
public void addAction(final Action action) {
|
||||
if (actions == null) {
|
||||
actions = new ArrayList<>();
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2018 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
import jakarta.persistence.ConstraintMode;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.ForeignKey;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.IdClass;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetMetadata;
|
||||
|
||||
/**
|
||||
* Meta data for {@link Target}.
|
||||
*/
|
||||
@NoArgsConstructor // Default constructor for JPA
|
||||
@Setter
|
||||
@Getter
|
||||
@IdClass(TargetMetadataCompositeKey.class)
|
||||
@Entity
|
||||
@Table(name = "sp_target_metadata")
|
||||
public class JpaTargetMetadata extends AbstractJpaMetaData implements TargetMetadata {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
||||
@JoinColumn(
|
||||
name = "target", nullable = false, updatable = false,
|
||||
foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_target_metadata_target"))
|
||||
private JpaTarget target;
|
||||
|
||||
/**
|
||||
* Creates a single metadata entry with the given key and value.
|
||||
*
|
||||
* @param key of the meta-data entry
|
||||
* @param value of the meta-data entry
|
||||
*/
|
||||
public JpaTargetMetadata(final String key, final String value) {
|
||||
super(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a single metadata entry with the given key and value for the
|
||||
* given {@link Target}.
|
||||
*
|
||||
* @param key of the meta-data entry
|
||||
* @param value of the meta-data entry
|
||||
* @param target the meta-data entry is associated with
|
||||
*/
|
||||
public JpaTargetMetadata(final String key, final String value, final Target target) {
|
||||
super(key, value);
|
||||
this.target = (JpaTarget) target;
|
||||
}
|
||||
|
||||
public TargetMetadataCompositeKey getId() {
|
||||
return new TargetMetadataCompositeKey(target.getId(), getKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = super.hashCode();
|
||||
result = prime * result + ((target == null) ? 0 : target.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
// exception squid:S2259 - obj is checked for null in super
|
||||
@SuppressWarnings("squid:S2259")
|
||||
public boolean equals(final Object obj) {
|
||||
if (!super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
final JpaTargetMetadata other = (JpaTargetMetadata) obj;
|
||||
if (target == null) {
|
||||
return other.target == null;
|
||||
} else {
|
||||
return target.equals(other.target);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2018 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* The Target Metadata composite key which contains the meta-data key and the ID of the Target itself.
|
||||
*/
|
||||
@NoArgsConstructor // Default constructor for JPA
|
||||
@Data
|
||||
public final class TargetMetadataCompositeKey implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String key;
|
||||
private Long target;
|
||||
|
||||
/**
|
||||
* @param target the target Id for this meta-data
|
||||
* @param key the key of the meta-data
|
||||
*/
|
||||
public TargetMetadataCompositeKey(final Long target, final String key) {
|
||||
this.target = target;
|
||||
this.key = key;
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.repository;
|
||||
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetMetadata;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.TargetMetadataCompositeKey;
|
||||
import org.eclipse.hawkbit.repository.model.TargetMetadata;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* {@link TargetMetadata} repository.
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public interface TargetMetadataRepository
|
||||
extends PagingAndSortingRepository<JpaTargetMetadata, TargetMetadataCompositeKey>,
|
||||
CrudRepository<JpaTargetMetadata, TargetMetadataCompositeKey>,
|
||||
JpaSpecificationExecutor<JpaTargetMetadata> {
|
||||
|
||||
/**
|
||||
* Counts the meta data entries that match the given target ID.
|
||||
* <p/>
|
||||
* No access control applied
|
||||
*
|
||||
* @param id of the target.
|
||||
* @return The number of matching meta data entries.
|
||||
*/
|
||||
long countByTargetId(@Param("id") Long id);
|
||||
}
|
||||
@@ -13,35 +13,31 @@ import java.util.function.ToLongFunction;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
||||
|
||||
/**
|
||||
* Helper class to check quotas.
|
||||
*/
|
||||
@NoArgsConstructor
|
||||
@Slf4j
|
||||
public final class QuotaHelper {
|
||||
|
||||
private static final String MAX_ASSIGNMENT_QUOTA_EXCEEDED = "Quota exceeded: Cannot assign %s entities at once. The maximum is %s.";
|
||||
|
||||
private QuotaHelper() {
|
||||
// no need to instantiate this class
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts the specified assignment quota.
|
||||
*
|
||||
* @param requested The number of entities that shall be assigned to the parent
|
||||
* entity.
|
||||
* @param limit The maximum number of entities that may be assigned to the
|
||||
* parent entity.
|
||||
* @param requested The number of entities that shall be assigned to the parent entity.
|
||||
* @param limit The maximum number of entities that may be assigned to the parent entity.
|
||||
* @param type The type of the entities that shall be assigned.
|
||||
* @param parentType The type of the parent entity.
|
||||
* @throws AssignmentQuotaExceededException if the assignment operation would cause the quota to be
|
||||
* exceeded
|
||||
* @throws AssignmentQuotaExceededException if the assignment operation would cause the quota to be exceeded
|
||||
*/
|
||||
public static void assertAssignmentQuota(final long requested, final long limit, @NotNull final Class<?> type,
|
||||
@NotNull final Class<?> parentType) {
|
||||
public static void assertAssignmentQuota(
|
||||
final long requested, final long limit,
|
||||
@NotNull final Class<?> type, @NotNull final Class<?> parentType) {
|
||||
assertAssignmentQuota(null, requested, limit, type.getSimpleName(), parentType.getSimpleName(), null);
|
||||
}
|
||||
|
||||
@@ -49,19 +45,18 @@ public final class QuotaHelper {
|
||||
* Asserts the specified assignment quota.
|
||||
*
|
||||
* @param parentId The ID of the parent entity.
|
||||
* @param requested The number of entities that shall be assigned to the parent
|
||||
* entity.
|
||||
* @param limit The maximum number of entities that may be assigned to the
|
||||
* parent entity.
|
||||
* @param requested The number of entities that shall be assigned to the parent entity.
|
||||
* @param limit The maximum number of entities that may be assigned to the parent entity.
|
||||
* @param type The type of the entities that shall be assigned.
|
||||
* @param parentType The type of the parent entity.
|
||||
* @param countFct Function to count the entities that are currently assigned to
|
||||
* the parent entity.
|
||||
* @throws AssignmentQuotaExceededException if the assignment operation would cause the quota to be
|
||||
* exceeded
|
||||
* @param countFct Function to count the entities that are currently assigned to the parent entity.
|
||||
* @throws AssignmentQuotaExceededException if the assignment operation would cause the quota to be exceeded
|
||||
*/
|
||||
public static <T> void assertAssignmentQuota(final T parentId, final long requested, final long limit,
|
||||
@NotNull final Class<?> type, @NotNull final Class<?> parentType, final ToLongFunction<T> countFct) {
|
||||
public static <T> void assertAssignmentQuota(
|
||||
final T parentId,
|
||||
final long requested, final long limit,
|
||||
@NotNull final Class<?> type, @NotNull final Class<?> parentType,
|
||||
final ToLongFunction<T> countFct) {
|
||||
assertAssignmentQuota(parentId, requested, limit, type.getSimpleName(), parentType.getSimpleName(), countFct);
|
||||
}
|
||||
|
||||
@@ -69,20 +64,18 @@ public final class QuotaHelper {
|
||||
* Asserts the specified assignment quota.
|
||||
*
|
||||
* @param parentId The ID of the parent entity.
|
||||
* @param requested The number of entities that shall be assigned to the parent
|
||||
* entity.
|
||||
* @param limit The maximum number of entities that may be assigned to the
|
||||
* parent entity.
|
||||
* @param requested The number of entities that shall be assigned to the parent entity.
|
||||
* @param limit The maximum number of entities that may be assigned to the parent entity.
|
||||
* @param type The type of the entities that shall be assigned.
|
||||
* @param parentType The type of the parent entity.
|
||||
* @param countFct Function to count the entities that are currently assigned to
|
||||
* the parent entity.
|
||||
* @throws AssignmentQuotaExceededException if the assignment operation would cause the quota to be
|
||||
* exceeded
|
||||
* @param countFct Function to count the entities that are currently assigned to the parent entity.
|
||||
* @throws AssignmentQuotaExceededException if the assignment operation would cause the quota to be exceeded
|
||||
*/
|
||||
public static <T> void assertAssignmentQuota(final T parentId, final long requested, final long limit,
|
||||
@NotNull final String type, @NotNull final String parentType, final ToLongFunction<T> countFct) {
|
||||
|
||||
public static <T> void assertAssignmentQuota(
|
||||
final T parentId,
|
||||
final long requested, final long limit,
|
||||
@NotNull final String type, @NotNull final String parentType,
|
||||
final ToLongFunction<T> countFct) {
|
||||
// check if the quota is unlimited
|
||||
if (limit <= 0) {
|
||||
log.debug("Quota 'Max {} entities per {}' is unlimited.", type, parentType);
|
||||
@@ -91,8 +84,9 @@ public final class QuotaHelper {
|
||||
|
||||
if (requested > limit) {
|
||||
final String parentIdStr = parentId != null ? String.valueOf(parentId) : "<new>";
|
||||
log.warn("Cannot assign {} {} entities to {} '{}' because of the configured quota limit {}.", requested,
|
||||
type, parentType, parentIdStr, limit);
|
||||
log.warn(
|
||||
"Cannot assign {} {} entities to {} '{}' because of the configured quota limit {}.",
|
||||
requested, type, parentType, parentIdStr, limit);
|
||||
throw new AssignmentQuotaExceededException(type, parentType, parentId, requested, limit);
|
||||
}
|
||||
|
||||
@@ -100,7 +94,8 @@ public final class QuotaHelper {
|
||||
final long currentCount = countFct.applyAsLong(parentId);
|
||||
if (currentCount + requested > limit) {
|
||||
log.warn(
|
||||
"Cannot assign {} {} entities to {} '{}' because of the configured quota limit {}. Currently, there are {} {} entities assigned.",
|
||||
"Cannot assign {} {} entities to {} '{}' because of the configured quota limit {}. " +
|
||||
"Currently, there are {} {} entities assigned.",
|
||||
requested, type, parentType, parentId, limit, currentCount, type);
|
||||
throw new AssignmentQuotaExceededException(type, parentType, parentId, requested, limit);
|
||||
}
|
||||
@@ -108,8 +103,7 @@ public final class QuotaHelper {
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the number of assignments in a request does not exceed the
|
||||
* limit.
|
||||
* Assert that the number of assignments in a request does not exceed the limit.
|
||||
*
|
||||
* @param requested the number of assignments that are to be made
|
||||
* @param limit the maximum number of assignments per request
|
||||
|
||||
@@ -303,7 +303,7 @@ public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest
|
||||
}
|
||||
|
||||
protected Set<TargetTag> getTargetTags(final String controllerId) {
|
||||
return targetManagement.getTagsByControllerId(controllerId);
|
||||
return targetManagement.getTags(controllerId);
|
||||
}
|
||||
|
||||
protected JpaRolloutGroup refresh(final RolloutGroup group) {
|
||||
|
||||
@@ -68,7 +68,7 @@ class DistributionSetManagementSecurityTest
|
||||
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
|
||||
void createMetaDataPermissionsCheck() {
|
||||
assertPermissions(
|
||||
() -> distributionSetManagement.putMetaData(1L, List.of(entityFactory.generateTargetMetadata("key", "value"))),
|
||||
() -> distributionSetManagement.putMetaData(1L, List.of(entityFactory.generateDsMetadata("key", "value"))),
|
||||
List.of(SpPermission.UPDATE_REPOSITORY));
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
package org.eclipse.hawkbit.repository.jpa.management;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import io.qameta.allure.Description;
|
||||
import io.qameta.allure.Feature;
|
||||
@@ -339,6 +340,26 @@ class TargetManagementSecurityTest extends AbstractJpaIntegrationTest {
|
||||
assertPermissions(() -> targetManagement.get(List.of(1L)), List.of(SpPermission.READ_TARGET));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
|
||||
void existsByControllerIdPermissionsCheck() {
|
||||
assertPermissions(() -> targetManagement.existsByControllerId("controllerId"), List.of(SpPermission.READ_TARGET));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
|
||||
void isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatablePermissionsCheck() {
|
||||
assertPermissions(
|
||||
() -> targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable("controllerId", 1L, "controllerId==id"),
|
||||
List.of(SpPermission.READ_TARGET, SpPermission.READ_REPOSITORY));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
|
||||
void getTagsPermissionsCheck() {
|
||||
assertPermissions(() -> targetManagement.getTags("controllerId"), List.of(SpPermission.READ_TARGET));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
|
||||
void getControllerAttributesPermissionsCheck() {
|
||||
@@ -368,72 +389,38 @@ class TargetManagementSecurityTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
@Test
|
||||
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
|
||||
void existsByControllerIdPermissionsCheck() {
|
||||
assertPermissions(() -> targetManagement.existsByControllerId("controllerId"), List.of(SpPermission.READ_TARGET));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
|
||||
void isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatablePermissionsCheck() {
|
||||
void createMetadataPermissionsCheck() {
|
||||
assertPermissions(
|
||||
() -> targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable("controllerId", 1L, "controllerId==id"),
|
||||
List.of(SpPermission.READ_TARGET, SpPermission.READ_REPOSITORY));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
|
||||
void getTagsByControllerIdPermissionsCheck() {
|
||||
assertPermissions(() -> targetManagement.getTagsByControllerId("controllerId"), List.of(SpPermission.READ_TARGET));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
|
||||
void createMetaDataPermissionsCheck() {
|
||||
assertPermissions(
|
||||
() -> targetManagement.createMetaData("controllerId", List.of(entityFactory.generateTargetMetadata("key", "value"))),
|
||||
() -> {
|
||||
targetManagement.createMetadata("controllerId", Map.of("key", "value"));
|
||||
return null;
|
||||
},
|
||||
List.of(SpPermission.UPDATE_REPOSITORY));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
|
||||
void deleteMetaDataPermissionsCheck() {
|
||||
assertPermissions(() -> {
|
||||
targetManagement.deleteMetaData("controllerId", "key");
|
||||
return null;
|
||||
}, List.of(SpPermission.UPDATE_REPOSITORY));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
|
||||
void countMetaDataByControllerIdPermissionsCheck() {
|
||||
assertPermissions(() -> targetManagement.countMetaDataByControllerId("controllerId"), List.of(SpPermission.READ_REPOSITORY));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
|
||||
void findMetaDataByControllerIdAndRsqlPermissionsCheck() {
|
||||
assertPermissions(() -> targetManagement.findMetaDataByControllerIdAndRsql(PAGE, "controllerId", "controllerId==id"),
|
||||
List.of(SpPermission.READ_REPOSITORY));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
|
||||
void getMetaDataByControllerIdPermissionsCheck() {
|
||||
assertPermissions(() -> targetManagement.getMetaDataByControllerId("controllerId", "key"), List.of(SpPermission.READ_REPOSITORY));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
|
||||
void findMetaDataByControllerIdPermissionsCheck() {
|
||||
assertPermissions(() -> targetManagement.findMetaDataByControllerId(PAGE, "controllerId"), List.of(SpPermission.READ_REPOSITORY));
|
||||
void getMetadataPermissionsCheck() {
|
||||
assertPermissions(() -> targetManagement.getMetadata("controllerId"), List.of(SpPermission.READ_REPOSITORY));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
|
||||
@WithUser(principal = "user", authorities = { SpPermission.UPDATE_REPOSITORY })
|
||||
void updateMetadataPermissionsCheck() {
|
||||
assertPermissions(() -> targetManagement.updateMetadata("controllerId", entityFactory.generateTargetMetadata("key", "value")),
|
||||
assertPermissions(() -> {
|
||||
targetManagement.updateMetadata("controllerId", "key", "value");
|
||||
return null;
|
||||
},
|
||||
List.of(SpPermission.UPDATE_REPOSITORY));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
|
||||
void deleteMetadataPermissionsCheck() {
|
||||
assertPermissions(() -> {
|
||||
targetManagement.deleteMetadata("controllerId", "key");
|
||||
return null;
|
||||
}, List.of(SpPermission.UPDATE_REPOSITORY));
|
||||
}
|
||||
}
|
||||
@@ -60,17 +60,14 @@ import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldExc
|
||||
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
|
||||
import org.eclipse.hawkbit.repository.model.MetaData;
|
||||
import org.eclipse.hawkbit.repository.model.NamedEntity;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.Tag;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.TargetTag;
|
||||
import org.eclipse.hawkbit.repository.model.TargetType;
|
||||
import org.eclipse.hawkbit.repository.model.TargetTypeAssignmentResult;
|
||||
@@ -81,7 +78,6 @@ import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
|
||||
import org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch;
|
||||
import org.eclipse.hawkbit.repository.test.util.WithUser;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Slice;
|
||||
|
||||
@@ -99,7 +95,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
|
||||
final Target target = testdataFactory.createTarget();
|
||||
assertThat(targetManagement.getByControllerID(NOT_EXIST_ID)).isNotPresent();
|
||||
assertThat(targetManagement.get(NOT_EXIST_IDL)).isNotPresent();
|
||||
assertThat(targetManagement.getMetaDataByControllerId(target.getControllerId(), NOT_EXIST_ID)).isNotPresent();
|
||||
assertThat(targetManagement.getMetadata(target.getControllerId()).get(NOT_EXIST_ID)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -113,20 +109,15 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
|
||||
final Target target = testdataFactory.createTarget();
|
||||
|
||||
verifyThrownExceptionBy(
|
||||
() -> targetManagement.assignTag(Collections.singletonList(target.getControllerId()), NOT_EXIST_IDL),
|
||||
"TargetTag");
|
||||
verifyThrownExceptionBy(() -> targetManagement.assignTag(Collections.singletonList(NOT_EXIST_ID), tag.getId()),
|
||||
"Target");
|
||||
() -> targetManagement.assignTag(Collections.singletonList(target.getControllerId()), NOT_EXIST_IDL), "TargetTag");
|
||||
verifyThrownExceptionBy(() -> targetManagement.assignTag(Collections.singletonList(NOT_EXIST_ID), tag.getId()), "Target");
|
||||
|
||||
verifyThrownExceptionBy(() -> targetManagement.findByTag(PAGE, NOT_EXIST_IDL), "TargetTag");
|
||||
verifyThrownExceptionBy(() -> targetManagement.findByRsqlAndTag(PAGE, "name==*", NOT_EXIST_IDL), "TargetTag");
|
||||
|
||||
verifyThrownExceptionBy(() -> targetManagement.countByAssignedDistributionSet(NOT_EXIST_IDL),
|
||||
"DistributionSet");
|
||||
verifyThrownExceptionBy(() -> targetManagement.countByInstalledDistributionSet(NOT_EXIST_IDL),
|
||||
"DistributionSet");
|
||||
verifyThrownExceptionBy(() -> targetManagement.existsByInstalledOrAssignedDistributionSet(NOT_EXIST_IDL),
|
||||
"DistributionSet");
|
||||
verifyThrownExceptionBy(() -> targetManagement.countByAssignedDistributionSet(NOT_EXIST_IDL), "DistributionSet");
|
||||
verifyThrownExceptionBy(() -> targetManagement.countByInstalledDistributionSet(NOT_EXIST_IDL), "DistributionSet");
|
||||
verifyThrownExceptionBy(() -> targetManagement.existsByInstalledOrAssignedDistributionSet(NOT_EXIST_IDL), "DistributionSet");
|
||||
|
||||
verifyThrownExceptionBy(() -> targetManagement.countByTargetFilterQuery(NOT_EXIST_IDL), "TargetFilterQuery");
|
||||
verifyThrownExceptionBy(() -> targetManagement.countByRsqlAndNonDSAndCompatibleAndUpdatable(NOT_EXIST_IDL, "name==*"),
|
||||
@@ -138,45 +129,29 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
|
||||
verifyThrownExceptionBy(
|
||||
() -> targetManagement.findByTargetFilterQueryAndNonDSAndCompatibleAndUpdatable(PAGE, NOT_EXIST_IDL, "name==*"),
|
||||
"DistributionSet");
|
||||
verifyThrownExceptionBy(() -> targetManagement.findByInRolloutGroupWithoutAction(PAGE, NOT_EXIST_IDL),
|
||||
"RolloutGroup");
|
||||
verifyThrownExceptionBy(() -> targetManagement.findByAssignedDistributionSet(PAGE, NOT_EXIST_IDL),
|
||||
"DistributionSet");
|
||||
verifyThrownExceptionBy(() -> targetManagement.findByInRolloutGroupWithoutAction(PAGE, NOT_EXIST_IDL), "RolloutGroup");
|
||||
verifyThrownExceptionBy(() -> targetManagement.findByAssignedDistributionSet(PAGE, NOT_EXIST_IDL), "DistributionSet");
|
||||
verifyThrownExceptionBy(
|
||||
() -> targetManagement.findByAssignedDistributionSetAndRsql(PAGE, NOT_EXIST_IDL, "name==*"),
|
||||
"DistributionSet");
|
||||
() -> targetManagement.findByAssignedDistributionSetAndRsql(PAGE, NOT_EXIST_IDL, "name==*"), "DistributionSet");
|
||||
|
||||
verifyThrownExceptionBy(() -> targetManagement.findByInstalledDistributionSet(PAGE, NOT_EXIST_IDL),
|
||||
"DistributionSet");
|
||||
verifyThrownExceptionBy(() -> targetManagement.findByInstalledDistributionSet(PAGE, NOT_EXIST_IDL), "DistributionSet");
|
||||
verifyThrownExceptionBy(
|
||||
() -> targetManagement.findByInstalledDistributionSetAndRsql(PAGE, NOT_EXIST_IDL, "name==*"),
|
||||
"DistributionSet");
|
||||
() -> targetManagement.findByInstalledDistributionSetAndRsql(PAGE, NOT_EXIST_IDL, "name==*"), "DistributionSet");
|
||||
|
||||
verifyThrownExceptionBy(() -> targetManagement
|
||||
.assignTag(Collections.singletonList(target.getControllerId()), Long.parseLong(NOT_EXIST_ID)), "TargetTag");
|
||||
verifyThrownExceptionBy(
|
||||
() -> targetManagement.assignTag(Collections.singletonList(NOT_EXIST_ID), tag.getId()),
|
||||
"Target");
|
||||
() -> targetManagement.assignTag(Collections.singletonList(NOT_EXIST_ID), tag.getId()), "Target");
|
||||
|
||||
verifyThrownExceptionBy(() -> targetManagement.unassignTag(List.of(NOT_EXIST_ID), tag.getId()), "Target");
|
||||
verifyThrownExceptionBy(() -> targetManagement.unassignTag(List.of(target.getControllerId()), NOT_EXIST_IDL),
|
||||
"TargetTag");
|
||||
verifyThrownExceptionBy(() -> targetManagement.unassignTag(List.of(target.getControllerId()), NOT_EXIST_IDL), "TargetTag");
|
||||
verifyThrownExceptionBy(() -> targetManagement.update(entityFactory.target().update(NOT_EXIST_ID)), "Target");
|
||||
|
||||
verifyThrownExceptionBy(() -> targetManagement.createMetaData(NOT_EXIST_ID,
|
||||
Collections.singletonList(entityFactory.generateTargetMetadata("123", "123"))), "Target");
|
||||
verifyThrownExceptionBy(() -> targetManagement.deleteMetaData(NOT_EXIST_ID, "xxx"), "Target");
|
||||
verifyThrownExceptionBy(() -> targetManagement.deleteMetaData(target.getControllerId(), NOT_EXIST_ID),
|
||||
"TargetMetadata");
|
||||
verifyThrownExceptionBy(() -> targetManagement.getMetaDataByControllerId(NOT_EXIST_ID, "xxx"), "Target");
|
||||
verifyThrownExceptionBy(() -> targetManagement.findMetaDataByControllerId(PAGE, NOT_EXIST_ID), "Target");
|
||||
verifyThrownExceptionBy(() -> targetManagement.findMetaDataByControllerIdAndRsql(PAGE, NOT_EXIST_ID, "key==*"),
|
||||
"Target");
|
||||
verifyThrownExceptionBy(
|
||||
() -> targetManagement.updateMetadata(NOT_EXIST_ID, entityFactory.generateTargetMetadata("xxx", "xxx")),
|
||||
"Target");
|
||||
verifyThrownExceptionBy(() -> targetManagement.updateMetadata(target.getControllerId(),
|
||||
entityFactory.generateTargetMetadata(NOT_EXIST_ID, "xxx")), "TargetMetadata");
|
||||
verifyThrownExceptionBy(() -> targetManagement.createMetadata(NOT_EXIST_ID, Map.of("123", "123")), "Target");
|
||||
verifyThrownExceptionBy(() -> targetManagement.deleteMetadata(NOT_EXIST_ID, "xxx"), "Target");
|
||||
verifyThrownExceptionBy(() -> targetManagement.deleteMetadata(target.getControllerId(), NOT_EXIST_ID), "Target");
|
||||
verifyThrownExceptionBy(() -> targetManagement.getMetadata(NOT_EXIST_ID).get("xxx"), "Target");
|
||||
verifyThrownExceptionBy(() -> targetManagement.updateMetadata(NOT_EXIST_ID, "xxx", "xxx"), "Target");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -764,68 +739,75 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
@Test
|
||||
@Description("Checks that metadata for a target can be created.")
|
||||
void createTargetMetadata() {
|
||||
final String knownKey = "targetMetaKnownKey";
|
||||
final String knownValue = "targetMetaKnownValue";
|
||||
|
||||
final Target target = testdataFactory.createTarget("targetIdWithMetadata");
|
||||
final JpaTargetMetadata createdMetadata = insertTargetMetadata(knownKey, knownValue, target);
|
||||
|
||||
assertThat(createdMetadata).isNotNull();
|
||||
assertThat(createdMetadata.getId().getKey()).isEqualTo(knownKey);
|
||||
assertThat(createdMetadata.getTarget().getControllerId()).isEqualTo(target.getControllerId());
|
||||
assertThat(createdMetadata.getTarget().getId()).isEqualTo(target.getId());
|
||||
assertThat(createdMetadata.getValue()).isEqualTo(knownValue);
|
||||
void createMetadata() {
|
||||
insertMetadata(
|
||||
"targetMetaKnownKey", "targetMetaKnownValue",
|
||||
testdataFactory.createTarget("targetIdWithMetadata")); // and validate
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verifies the enforcement of the metadata quota per target.")
|
||||
void createTargetMetadataUntilQuotaIsExceeded() {
|
||||
void createMetadataUntilQuotaIsExceeded() {
|
||||
// add meta-data one by one
|
||||
final Target target1 = testdataFactory.createTarget("target1");
|
||||
final int maxMetaData = quotaManagement.getMaxMetaDataEntriesPerTarget();
|
||||
for (int i = 0; i < maxMetaData; ++i) {
|
||||
assertThat(insertTargetMetadata("k" + i, "v" + i, target1)).isNotNull();
|
||||
insertMetadata("k" + i, "v" + i, target1);
|
||||
}
|
||||
|
||||
// quota exceeded
|
||||
assertThatExceptionOfType(AssignmentQuotaExceededException.class)
|
||||
.isThrownBy(() -> insertTargetMetadata("k" + maxMetaData, "v" + maxMetaData, target1));
|
||||
.isThrownBy(() -> insertMetadata("k" + maxMetaData, "v" + maxMetaData, target1));
|
||||
|
||||
// add multiple meta data entries at once
|
||||
final Target target2 = testdataFactory.createTarget("target2");
|
||||
final List<MetaData> metaData2 = new ArrayList<>();
|
||||
for (int i = 0; i < maxMetaData + 1; ++i) {
|
||||
metaData2.add(new JpaTargetMetadata("k" + i, "v" + i, target2));
|
||||
final Map<String, String> metaData2 = new HashMap<>();
|
||||
for (int i = 0; i < maxMetaData + 1; i++) {
|
||||
metaData2.put("k" + i, "v" + i);
|
||||
}
|
||||
// verify quota is exceeded
|
||||
final String target2ControllerId = target2.getControllerId();
|
||||
assertThatExceptionOfType(AssignmentQuotaExceededException.class)
|
||||
.isThrownBy(() -> targetManagement.createMetaData(target2ControllerId, metaData2));
|
||||
.isThrownBy(() -> targetManagement.createMetadata(target2ControllerId, metaData2));
|
||||
|
||||
// add some meta data entries
|
||||
final Target target3 = testdataFactory.createTarget("target3");
|
||||
final int firstHalf = maxMetaData / 2;
|
||||
for (int i = 0; i < firstHalf; ++i) {
|
||||
insertTargetMetadata("k" + i, "v" + i, target3);
|
||||
for (int i = 0; i < firstHalf; i++) {
|
||||
insertMetadata("k" + i, "v" + i, target3);
|
||||
}
|
||||
// add too many data entries
|
||||
final int secondHalf = maxMetaData - firstHalf;
|
||||
final List<MetaData> metaData3 = new ArrayList<>();
|
||||
for (int i = 0; i < secondHalf + 1; ++i) {
|
||||
metaData3.add(new JpaTargetMetadata("kk" + i, "vv" + i, target3));
|
||||
final Map<String, String> metaData3 = new HashMap<>();
|
||||
for (int i = 0; i < secondHalf + 1; i++) {
|
||||
metaData3.put("kk" + i, "vv" + i);
|
||||
}
|
||||
// verify quota is exceeded
|
||||
final String target3ControllerId = target3.getControllerId();
|
||||
assertThatExceptionOfType(AssignmentQuotaExceededException.class)
|
||||
.isThrownBy(() -> targetManagement.createMetaData(target3ControllerId, metaData3));
|
||||
.isThrownBy(() -> targetManagement.createMetadata(target3ControllerId, metaData3));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
@Description("Queries and loads the metadata related to a given target.")
|
||||
void getMetadata() {
|
||||
// create targets
|
||||
final Target target1 = createTargetWithMetadata("target1", 10);
|
||||
final Target target2 = createTargetWithMetadata("target2", 8);
|
||||
|
||||
final Map<String, String> metadataOfTarget1 = targetManagement.getMetadata(target1.getControllerId());
|
||||
final Map<String, String> metadataOfTarget2 = targetManagement.getMetadata(target2.getControllerId());
|
||||
|
||||
assertThat(metadataOfTarget1).hasSize(10);
|
||||
assertThat(metadataOfTarget2).hasSize(8);
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithUser(allSpPermissions = true)
|
||||
@Description("Checks that metadata for a target can be updated.")
|
||||
void updateTargetMetadata() {
|
||||
void updateMetadata() {
|
||||
final String knownKey = "myKnownKey";
|
||||
final String knownValue = "myKnownValue";
|
||||
final String knownUpdateValue = "myNewUpdatedValue";
|
||||
@@ -836,26 +818,36 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
|
||||
assertThat(target.getOptLockRevision()).isEqualTo(1);
|
||||
|
||||
// create target meta data entry
|
||||
insertTargetMetadata(knownKey, knownValue, target);
|
||||
insertMetadata(knownKey, knownValue, target);
|
||||
|
||||
Target changedLockRevisionTarget = targetManagement.get(target.getId()).orElseThrow(NoSuchElementException::new);
|
||||
final Target changedLockRevisionTarget = targetManagement.get(target.getId()).orElseThrow(NoSuchElementException::new);
|
||||
assertThat(changedLockRevisionTarget.getOptLockRevision()).isEqualTo(2);
|
||||
|
||||
// update the target metadata
|
||||
final JpaTargetMetadata updated = (JpaTargetMetadata) targetManagement.updateMetadata(
|
||||
target.getControllerId(), entityFactory.generateTargetMetadata(knownKey, knownUpdateValue));
|
||||
// we are updating the target meta-data so also modifying the base
|
||||
// software module so opt lock revision must be three
|
||||
changedLockRevisionTarget = targetManagement.get(target.getId()).orElseThrow(NoSuchElementException::new);
|
||||
assertThat(changedLockRevisionTarget.getOptLockRevision()).isEqualTo(3);
|
||||
assertThat(changedLockRevisionTarget.getLastModifiedAt()).isPositive();
|
||||
targetManagement.updateMetadata(target.getControllerId(), knownKey, knownUpdateValue);
|
||||
// we are updating the target meta-data so also modifying the target so opt lock revision must be three
|
||||
final Target changedLockRevisionTarget2 = targetManagement.get(target.getId()).orElseThrow(NoSuchElementException::new);
|
||||
assertThat(changedLockRevisionTarget2.getOptLockRevision()).isEqualTo(3);
|
||||
assertThat(changedLockRevisionTarget2.getLastModifiedAt()).isPositive();
|
||||
|
||||
// verify updated meta data contains the updated value
|
||||
assertThat(updated).isNotNull();
|
||||
assertThat(updated.getValue()).isEqualTo(knownUpdateValue);
|
||||
assertThat(updated.getId().getKey()).isEqualTo(knownKey);
|
||||
assertThat(updated.getTarget().getControllerId()).isEqualTo(target.getControllerId());
|
||||
assertThat(updated.getTarget().getId()).isEqualTo(target.getId());
|
||||
assertThat(targetManagement.getMetadata(target.getControllerId()).get(knownKey)).isEqualTo(knownUpdateValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Queries and loads the metadata related to a given target.")
|
||||
void deleteMetadata() {
|
||||
final String knownKey = "myKnownKey";
|
||||
final String knownValue = "myKnownValue";
|
||||
|
||||
// create target
|
||||
final String controllerId = createTargetWithMetadata("target1", 10).getControllerId();
|
||||
targetManagement.createMetadata(controllerId, Map.of(knownKey, knownValue));
|
||||
|
||||
targetManagement.deleteMetadata(controllerId, knownKey);
|
||||
// already removed
|
||||
assertThatExceptionOfType(EntityNotFoundException.class)
|
||||
.isThrownBy(() -> targetManagement.deleteMetadata(controllerId, knownKey));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -963,26 +955,6 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
|
||||
checkTargetsHaveType(typeATargets, typeB);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Queries and loads the metadata related to a given target.")
|
||||
void findAllTargetMetadataByControllerId() {
|
||||
// create targets
|
||||
final Target target1 = createTargetWithMetadata("target1", 10);
|
||||
final Target target2 = createTargetWithMetadata("target2", 8);
|
||||
|
||||
final Page<TargetMetadata> metadataOfTarget1 = targetManagement
|
||||
.findMetaDataByControllerId(PageRequest.of(0, 100), target1.getControllerId());
|
||||
|
||||
final Page<TargetMetadata> metadataOfTarget2 = targetManagement
|
||||
.findMetaDataByControllerId(PageRequest.of(0, 100), target2.getControllerId());
|
||||
|
||||
assertThat(metadataOfTarget1.getNumberOfElements()).isEqualTo(10);
|
||||
assertThat(metadataOfTarget1.getTotalElements()).isEqualTo(10);
|
||||
|
||||
assertThat(metadataOfTarget2.getNumberOfElements()).isEqualTo(8);
|
||||
assertThat(metadataOfTarget2.getTotalElements()).isEqualTo(8);
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithUser(allSpPermissions = true)
|
||||
@Description("Checks that target type is not assigned to target if invalid.")
|
||||
@@ -1419,11 +1391,9 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
|
||||
}
|
||||
}
|
||||
|
||||
private JpaTargetMetadata insertTargetMetadata(final String knownKey, final String knownValue,
|
||||
final Target target) {
|
||||
final JpaTargetMetadata metadata = new JpaTargetMetadata(knownKey, knownValue, target);
|
||||
return (JpaTargetMetadata) targetManagement
|
||||
.createMetaData(target.getControllerId(), Collections.singletonList(metadata)).get(0);
|
||||
private void insertMetadata(final String knownKey, final String knownValue, final Target target) {
|
||||
targetManagement.createMetadata(target.getControllerId(), Map.of(knownKey, knownValue));
|
||||
assertThat(targetManagement.getMetadata(target.getControllerId()).get(knownKey)).isEqualTo(knownValue);
|
||||
}
|
||||
|
||||
private void checkTargetsHaveType(final List<Target> targets, final TargetType type) {
|
||||
@@ -1440,7 +1410,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
|
||||
final Target target = testdataFactory.createTarget(controllerId);
|
||||
|
||||
for (int index = 1; index <= count; index++) {
|
||||
insertTargetMetadata("key" + index, controllerId + "-value" + index, target);
|
||||
insertMetadata("key" + index, controllerId + "-value" + index, target);
|
||||
}
|
||||
|
||||
return target;
|
||||
@@ -1450,7 +1420,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
|
||||
final Target target = testdataFactory.createTarget(controllerId, controllerId, targetTypeId);
|
||||
|
||||
for (int index = 1; index <= count; index++) {
|
||||
insertTargetMetadata("key" + index, controllerId + "-value" + index, target);
|
||||
insertMetadata("key" + index, controllerId + "-value" + index, target);
|
||||
}
|
||||
|
||||
return target;
|
||||
|
||||
@@ -79,7 +79,7 @@ class RSQLTargetFieldTest extends AbstractJpaIntegrationTest {
|
||||
.description("targetDesc123"));
|
||||
target = controllerManagement.updateControllerAttributes(target.getControllerId(), Map.of("revision", "1.1"), null);
|
||||
target = controllerManagement.findOrRegisterTargetIfItDoesNotExist(target.getControllerId(), LOCALHOST);
|
||||
createTargetMetadata(target.getControllerId(), entityFactory.generateTargetMetadata("metaKey", "metaValue"));
|
||||
targetManagement.createMetadata(target.getControllerId(), Map.of("metaKey", "metaValue"));
|
||||
assignDistributionSet(ds.getId(), target.getControllerId());
|
||||
targetManagement.assignType(target.getControllerId(), targetType1.getId());
|
||||
|
||||
@@ -89,7 +89,7 @@ class RSQLTargetFieldTest extends AbstractJpaIntegrationTest {
|
||||
.description("targetId1234"));
|
||||
target2 = controllerManagement.updateControllerAttributes(target2.getControllerId(), Map.of("revision", "1.2"), null);
|
||||
targetManagement.assignType(target2.getControllerId(), targetType2.getId());
|
||||
createTargetMetadata(target2.getControllerId(), entityFactory.generateTargetMetadata("metaKey", "value"));
|
||||
targetManagement.createMetadata(target2.getControllerId(), Map.of("metaKey", "value"));
|
||||
target2 = controllerManagement.findOrRegisterTargetIfItDoesNotExist(target2.getControllerId(), LOCALHOST);
|
||||
targetManagement.assignTag(Arrays.asList(target.getControllerId(), target2.getControllerId()), targetTag.getId());
|
||||
|
||||
@@ -270,7 +270,7 @@ class RSQLTargetFieldTest extends AbstractJpaIntegrationTest {
|
||||
@Test
|
||||
@Description("Test filter target by metadata")
|
||||
void testFilterByMetadata() {
|
||||
createTargetMetadata(testdataFactory.createTarget().getControllerId(), entityFactory.generateTargetMetadata("key.dot", "value.dot"));
|
||||
targetManagement.createMetadata(testdataFactory.createTarget().getControllerId(), Map.of("key.dot", "value.dot"));
|
||||
|
||||
assertRSQLQuery(TargetFields.METADATA.name() + ".metaKey==metaValue", 1);
|
||||
assertRSQLQuery(TargetFields.METADATA.name() + ".metaKey==null", 0); // "null" check
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.rsql;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import io.qameta.allure.Description;
|
||||
import io.qameta.allure.Feature;
|
||||
import io.qameta.allure.Story;
|
||||
import org.eclipse.hawkbit.repository.TargetMetadataFields;
|
||||
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
|
||||
import org.eclipse.hawkbit.repository.model.MetaData;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetMetadata;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
|
||||
@Feature("Component Tests - Repository")
|
||||
@Story("RSQL filter target metadata")
|
||||
class RSQLTargetMetadataFieldsTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
private String controllerId;
|
||||
|
||||
@BeforeEach
|
||||
void setupBeforeTest() {
|
||||
final Target target = testdataFactory.createTarget("target");
|
||||
controllerId = target.getControllerId();
|
||||
|
||||
final List<MetaData> metadata = new ArrayList<>(5);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
metadata.add(entityFactory.generateTargetMetadata("" + i, "" + i));
|
||||
}
|
||||
|
||||
targetManagement.createMetaData(controllerId, metadata);
|
||||
|
||||
targetManagement.createMetaData(controllerId,
|
||||
Arrays.asList(entityFactory.generateTargetMetadata("emptyValueTest", null)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Test filter target metadata by key")
|
||||
void testFilterByParameterKey() {
|
||||
assertRSQLQuery(TargetMetadataFields.KEY.name() + "==1", 1);
|
||||
assertRSQLQuery(TargetMetadataFields.KEY.name() + "!=1", 5);
|
||||
assertRSQLQuery(TargetMetadataFields.KEY.name() + "=in=(1,2)", 2);
|
||||
assertRSQLQuery(TargetMetadataFields.KEY.name() + "=out=(1,2)", 4);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Test filter target metadata by value")
|
||||
void testFilterByParameterValue() {
|
||||
assertRSQLQuery(TargetMetadataFields.VALUE.name() + "==''", 1);
|
||||
assertRSQLQuery(TargetMetadataFields.VALUE.name() + "!=''", 5);
|
||||
assertRSQLQuery(TargetMetadataFields.VALUE.name() + "==1", 1);
|
||||
assertRSQLQuery(TargetMetadataFields.VALUE.name() + "!=1", 5);
|
||||
assertRSQLQuery(TargetMetadataFields.VALUE.name() + "=in=(1,2)", 2);
|
||||
assertRSQLQuery(TargetMetadataFields.VALUE.name() + "=out=(1,2)", 4);
|
||||
}
|
||||
|
||||
private void assertRSQLQuery(final String rsqlParam, final long expectedEntities) {
|
||||
|
||||
final Page<TargetMetadata> findEnitity = targetManagement
|
||||
.findMetaDataByControllerIdAndRsql(PageRequest.of(0, 100), controllerId, rsqlParam);
|
||||
final long countAllEntities = findEnitity.getTotalElements();
|
||||
assertThat(findEnitity).isNotNull();
|
||||
assertThat(countAllEntities).isEqualTo(expectedEntities);
|
||||
}
|
||||
}
|
||||
@@ -12,10 +12,10 @@
|
||||
|
||||
logging.level.org.eclipse.persistence=ERROR
|
||||
## Uncomment to see the debug of persistence, e.g. to see the generated SQLs
|
||||
logging.level.org.eclipse.persistence=DEBUG
|
||||
spring.jpa.properties.eclipselink.logging.level=FINE
|
||||
spring.jpa.properties.eclipselink.logging.level.sql=FINE
|
||||
spring.jpa.properties.eclipselink.logging.parameters=true
|
||||
#logging.level.org.eclipse.persistence=DEBUG
|
||||
#spring.jpa.properties.eclipselink.logging.level=FINE
|
||||
#spring.jpa.properties.eclipselink.logging.level.sql=FINE
|
||||
#spring.jpa.properties.eclipselink.logging.parameters=true
|
||||
|
||||
## Enable EclipseLink performance monitor (monitoring and profile)
|
||||
#spring.jpa.properties.eclipselink.profiler=PerformanceMonitor
|
||||
|
||||
@@ -424,10 +424,6 @@ public abstract class AbstractIntegrationTest {
|
||||
return distributionSetManagement.putMetaData(dsId, md);
|
||||
}
|
||||
|
||||
protected void createTargetMetadata(final String controllerId, final MetaData md) {
|
||||
createTargetMetadata(controllerId, Collections.singletonList(md));
|
||||
}
|
||||
|
||||
protected Long getOsModule(final DistributionSet ds) {
|
||||
return ds.findFirstModuleByType(osType).orElseThrow(NoSuchElementException::new).getId();
|
||||
}
|
||||
@@ -504,8 +500,4 @@ public abstract class AbstractIntegrationTest {
|
||||
throw new ArtifactStoreException("Cannot create temp file", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void createTargetMetadata(final String controllerId, final List<MetaData> md) {
|
||||
targetManagement.createMetaData(controllerId, md);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user