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:
Avgustin Marinov
2025-05-21 11:26:02 +03:00
committed by GitHub
parent 424520bb72
commit ceba4f5cfb
29 changed files with 490 additions and 1107 deletions

View File

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

View File

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

View File

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

View File

@@ -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.",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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}
*/

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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