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:
@@ -19,7 +19,7 @@ import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* The representation of an meta data in the REST API for POST/Create.
|
||||
* The representation of a meta-data in the REST API for POST/Create.
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@@ -29,9 +29,9 @@ import lombok.experimental.Accessors;
|
||||
public class MgmtMetadata {
|
||||
|
||||
@JsonProperty(required = true)
|
||||
@Schema(description = "Metadata property key", example = "someKnownKey")
|
||||
@Schema(description = "Metadata property key", example = "country")
|
||||
private String key;
|
||||
|
||||
@Schema(description = "Metadata property value", example = "someKnownKeyValue")
|
||||
@Schema(description = "Metadata property value", example = "DE")
|
||||
private String value;
|
||||
}
|
||||
@@ -38,6 +38,7 @@ import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTargetAutoConfirmUpdate;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTargetRequestBody;
|
||||
import org.eclipse.hawkbit.rest.json.model.ExceptionInfo;
|
||||
import org.springframework.hateoas.MediaTypes;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
@@ -47,6 +48,7 @@ import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
/**
|
||||
* API for handling target operations.
|
||||
@@ -727,15 +729,47 @@ public interface MgmtTargetRestApi {
|
||||
produces = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
|
||||
ResponseEntity<List<MgmtTag>> getTags(@PathVariable("targetId") String targetId);
|
||||
|
||||
/**
|
||||
* Creates a list of metadata for a specific target.
|
||||
*
|
||||
* @param targetId the ID of the targetId to create metadata for
|
||||
* @param metadataRest the list of metadata entries to create
|
||||
*/
|
||||
@Operation(summary = "Create a list of metadata for a specific target", description = "Create a list of metadata entries Required permissions: READ_REPOSITORY and UPDATE_TARGET")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "201", description = "Successfully created"),
|
||||
@ApiResponse(responseCode = "400", description = "Bad Request - e.g. invalid parameters",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ExceptionInfo.class))),
|
||||
@ApiResponse(responseCode = "401", description = "The request requires user authentication.",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||
@ApiResponse(responseCode = "403",
|
||||
description = "Insufficient permissions, entity is not allowed to be changed (i.e. read-only) or " +
|
||||
"data volume restriction applies.",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||
@ApiResponse(responseCode = "404", description = "Target not found", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||
@ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||
@ApiResponse(responseCode = "406", description = "In case accept header is specified and not application/json.",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||
@ApiResponse(responseCode = "409", description = "E.g. in case an entity is created or modified by another " +
|
||||
"user in another request at the same time. You may retry your modification request.",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||
@ApiResponse(responseCode = "415", description = "The request was attempt with a media-type which is not " +
|
||||
"supported by the server for this resource.",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||
@ApiResponse(responseCode = "429", description = "Too many requests. The server will refuse further attempts " +
|
||||
"and the client has to wait another second.",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true)))
|
||||
})
|
||||
@PostMapping(value = MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/metadata",
|
||||
consumes = { MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE })
|
||||
@ResponseStatus(HttpStatus.CREATED)
|
||||
void createMetadata(@PathVariable("targetId") String targetId, @RequestBody List<MgmtMetadata> metadataRest);
|
||||
|
||||
/**
|
||||
* Gets a paged list of metadata for a target.
|
||||
*
|
||||
* @param targetId the ID of the target for the metadata
|
||||
* @param pagingOffsetParam the offset of list of targets for pagination, might not be present in the rest request then default value will
|
||||
* be applied
|
||||
* @param pagingLimitParam the limit of the paged request, might not be present in the rest request then default value will be applied
|
||||
* @param sortParam the sorting parameter in the request URL, syntax {@code field:direction, field:direction}
|
||||
* @param rsqlParam the search parameter in the request URL, syntax {@code q=key==abc}
|
||||
* @return status OK if get request is successful with the paged list of metadata
|
||||
*/
|
||||
@Operation(summary = "Return metadata for specific target", description = "Get a paged list of metadata for a target. Required permission: READ_REPOSITORY")
|
||||
@@ -758,32 +792,9 @@ public interface MgmtTargetRestApi {
|
||||
"and the client has to wait another second.",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true)))
|
||||
})
|
||||
@GetMapping(value = MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/metadata", produces = {
|
||||
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
|
||||
ResponseEntity<PagedList<MgmtMetadata>> getMetadata(
|
||||
@PathVariable("targetId") String targetId,
|
||||
@RequestParam(
|
||||
value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET,
|
||||
defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET)
|
||||
@Schema(description = "The paging offset (default is 0)")
|
||||
int pagingOffsetParam,
|
||||
@RequestParam(
|
||||
value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT,
|
||||
defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT)
|
||||
@Schema(description = "The maximum number of entries in a page (default is 50)")
|
||||
int pagingLimitParam,
|
||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false)
|
||||
@Schema(description = """
|
||||
The query parameter sort allows to define the sort order for the result of a query. A sort criteria
|
||||
consists of the name of a field and the sort direction (ASC for ascending and DESC descending).
|
||||
The sequence of the sort criteria (multiple can be used) defines the sort order of the entities
|
||||
in the result.""")
|
||||
String sortParam,
|
||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false)
|
||||
@Schema(description = """
|
||||
Query fields based on the Feed Item Query Language (FIQL). See Entity Definitions for
|
||||
available fields.""")
|
||||
String rsqlParam);
|
||||
@GetMapping(value = MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/metadata",
|
||||
produces = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
|
||||
ResponseEntity<PagedList<MgmtMetadata>> getMetadata(@PathVariable("targetId") String targetId);
|
||||
|
||||
/**
|
||||
* Gets a single metadata value for a specific key of a target.
|
||||
@@ -824,7 +835,6 @@ public interface MgmtTargetRestApi {
|
||||
* @param targetId the ID of the target to update the metadata entry
|
||||
* @param metadataKey the key of the metadata to update the value
|
||||
* @param metadata update body
|
||||
* @return status OK if the update request is successful and the updated metadata result
|
||||
*/
|
||||
@Operation(summary = "Updates a single metadata value of a target", description = "Update a single metadata value for speficic key. Required permission: UPDATE_REPOSITORY")
|
||||
@ApiResponses(value = {
|
||||
@@ -834,8 +844,7 @@ public interface MgmtTargetRestApi {
|
||||
@ApiResponse(responseCode = "401", description = "The request requires user authentication.",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||
@ApiResponse(responseCode = "403",
|
||||
description = "Insufficient permissions, entity is not allowed to be changed (i.e. read-only) or " +
|
||||
"data volume restriction applies.",
|
||||
description = "Insufficient permissions, entity is not allowed to be changed (i.e. read-only) or data volume restriction applies.",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||
@ApiResponse(responseCode = "404", description = "Target not found.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||
@ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.",
|
||||
@@ -852,9 +861,9 @@ public interface MgmtTargetRestApi {
|
||||
"and the client has to wait another second.",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true)))
|
||||
})
|
||||
@PutMapping(value = MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/metadata/{metadataKey}",
|
||||
produces = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
|
||||
ResponseEntity<MgmtMetadata> updateMetadata(
|
||||
@PutMapping(value = MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/metadata/{metadataKey}")
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
void updateMetadata(
|
||||
@PathVariable("targetId") String targetId,
|
||||
@PathVariable("metadataKey") String metadataKey,
|
||||
@RequestBody MgmtMetadataBodyPut metadata);
|
||||
@@ -887,49 +896,8 @@ public interface MgmtTargetRestApi {
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true)))
|
||||
})
|
||||
@DeleteMapping(value = MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/metadata/{metadataKey}")
|
||||
ResponseEntity<Void> deleteMetadata(
|
||||
@PathVariable("targetId") String targetId,
|
||||
@PathVariable("metadataKey") String metadataKey);
|
||||
|
||||
/**
|
||||
* Creates a list of metadata for a specific target.
|
||||
*
|
||||
* @param targetId the ID of the targetId to create metadata for
|
||||
* @param metadataRest the list of metadata entries to create
|
||||
* @return status created if post request is successful with the value of the created metadata
|
||||
*/
|
||||
@Operation(summary = "Create a list of metadata for a specific target", description = "Create a list of metadata entries Required permissions: READ_REPOSITORY and UPDATE_TARGET")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "201", description = "Successfully created"),
|
||||
@ApiResponse(responseCode = "400", description = "Bad Request - e.g. invalid parameters",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ExceptionInfo.class))),
|
||||
@ApiResponse(responseCode = "401", description = "The request requires user authentication.",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||
@ApiResponse(responseCode = "403",
|
||||
description = "Insufficient permissions, entity is not allowed to be changed (i.e. read-only) or " +
|
||||
"data volume restriction applies.",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||
@ApiResponse(responseCode = "404", description = "Target not found", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||
@ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||
@ApiResponse(responseCode = "406", description = "In case accept header is specified and not application/json.",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||
@ApiResponse(responseCode = "409", description = "E.g. in case an entity is created or modified by another " +
|
||||
"user in another request at the same time. You may retry your modification request.",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||
@ApiResponse(responseCode = "415", description = "The request was attempt with a media-type which is not " +
|
||||
"supported by the server for this resource.",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||
@ApiResponse(responseCode = "429", description = "Too many requests. The server will refuse further attempts " +
|
||||
"and the client has to wait another second.",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true)))
|
||||
})
|
||||
@PostMapping(value = MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/metadata",
|
||||
consumes = { MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE },
|
||||
produces = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
|
||||
ResponseEntity<List<MgmtMetadata>> createMetadata(
|
||||
@PathVariable("targetId") String targetId,
|
||||
@RequestBody List<MgmtMetadata> metadataRest);
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
void deleteMetadata(@PathVariable("targetId") String targetId, @PathVariable("metadataKey") String metadataKey);
|
||||
|
||||
/**
|
||||
* Get the current auto-confirm state for a specific target.
|
||||
@@ -945,8 +913,7 @@ public interface MgmtTargetRestApi {
|
||||
@ApiResponse(responseCode = "401", description = "The request requires user authentication.",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||
@ApiResponse(responseCode = "403",
|
||||
description = "Insufficient permissions, entity is not allowed to be changed (i.e. read-only) or " +
|
||||
"data volume restriction applies.",
|
||||
description = "Insufficient permissions, entity is not allowed to be changed (i.e. read-only) or data volume restriction applies.",
|
||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||
@ApiResponse(responseCode = "404", description = "Target not found.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||
@ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.",
|
||||
|
||||
@@ -18,7 +18,9 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
@@ -46,11 +48,9 @@ import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
||||
import org.eclipse.hawkbit.repository.model.AutoConfirmationStatus;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.MetaData;
|
||||
import org.eclipse.hawkbit.repository.model.PollStatus;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetMetadata;
|
||||
import org.eclipse.hawkbit.rest.json.model.ResponseList;
|
||||
import org.eclipse.hawkbit.util.IpUtil;
|
||||
import org.eclipse.hawkbit.utils.TenantConfigHelper;
|
||||
@@ -79,10 +79,8 @@ public final class MgmtTargetMapper {
|
||||
MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT_VALUE,
|
||||
ActionFields.ID.getJpaEntityFieldName() + ":" + SortDirection.DESC, null))
|
||||
.withRel(MgmtRestConstants.TARGET_V1_ACTIONS).expand());
|
||||
response.add(linkTo(methodOn(MgmtTargetRestApi.class).getMetadata(response.getControllerId(),
|
||||
MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET_VALUE,
|
||||
MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT_VALUE, null, null)).withRel("metadata")
|
||||
.expand());
|
||||
response.add(linkTo(methodOn(MgmtTargetRestApi.class).getMetadata(response.getControllerId()))
|
||||
.withRel("metadata").expand());
|
||||
if (response.getTargetType() != null) {
|
||||
response.add(linkTo(methodOn(MgmtTargetTypeRestApi.class).getTargetType(response.getTargetType()))
|
||||
.withRel(MgmtRestConstants.TARGET_V1_ASSIGNED_TARGET_TYPE).expand());
|
||||
@@ -196,19 +194,14 @@ public final class MgmtTargetMapper {
|
||||
.toList();
|
||||
}
|
||||
|
||||
static List<MetaData> fromRequestTargetMetadata(final List<MgmtMetadata> metadata,
|
||||
final EntityFactory entityFactory) {
|
||||
if (metadata == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return metadata.stream().map(
|
||||
metadataRest -> entityFactory.generateTargetMetadata(metadataRest.getKey(), metadataRest.getValue()))
|
||||
.toList();
|
||||
static Map<String, String> fromRequestMetadata(final List<MgmtMetadata> metadata) {
|
||||
return metadata == null
|
||||
? Collections.emptyMap()
|
||||
: metadata.stream().collect(Collectors.toMap(MgmtMetadata::getKey, MgmtMetadata::getValue));
|
||||
}
|
||||
|
||||
static List<MgmtActionStatus> toActionStatusRestResponse(final Collection<ActionStatus> actionStatus,
|
||||
final DeploymentManagement deploymentManagement) {
|
||||
static List<MgmtActionStatus> toActionStatusRestResponse(
|
||||
final Collection<ActionStatus> actionStatus, final DeploymentManagement deploymentManagement) {
|
||||
if (actionStatus == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
@@ -308,15 +301,15 @@ public final class MgmtTargetMapper {
|
||||
return actions.stream().map(action -> toResponse(targetId, action)).toList();
|
||||
}
|
||||
|
||||
static MgmtMetadata toResponseTargetMetadata(final TargetMetadata metadata) {
|
||||
static MgmtMetadata toResponseMetadata(final String key, final String value) {
|
||||
final MgmtMetadata metadataRest = new MgmtMetadata();
|
||||
metadataRest.setKey(metadata.getKey());
|
||||
metadataRest.setValue(metadata.getValue());
|
||||
metadataRest.setKey(key);
|
||||
metadataRest.setValue(value);
|
||||
return metadataRest;
|
||||
}
|
||||
|
||||
static List<MgmtMetadata> toResponseTargetMetadata(final List<TargetMetadata> metadata) {
|
||||
return metadata.stream().map(MgmtTargetMapper::toResponseTargetMetadata).toList();
|
||||
static List<MgmtMetadata> toResponseMetadata(final Map<String, String> metadata) {
|
||||
return metadata.entrySet().stream().map(e -> toResponseMetadata(e.getKey(), e.getValue())).toList();
|
||||
}
|
||||
|
||||
private static void addPollStatus(final Target target, final MgmtTarget targetRest, final Function<Target, PollStatus> pollStatusResolver) {
|
||||
|
||||
@@ -57,7 +57,6 @@ import org.eclipse.hawkbit.repository.model.ActionStatus;
|
||||
import org.eclipse.hawkbit.repository.model.DeploymentRequest;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.TargetTag;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.utils.TenantConfigHelper;
|
||||
@@ -421,57 +420,39 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
|
||||
|
||||
@Override
|
||||
public ResponseEntity<List<MgmtTag>> getTags(final String targetId) {
|
||||
final Set<TargetTag> tags = targetManagement.getTagsByControllerId(targetId);
|
||||
final Set<TargetTag> tags = targetManagement.getTags(targetId);
|
||||
return ResponseEntity.ok(MgmtTagMapper.toResponse(tags == null ? Collections.emptyList() : tags.stream().toList()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<PagedList<MgmtMetadata>> getMetadata(
|
||||
final String targetId,
|
||||
final int pagingOffsetParam, final int pagingLimitParam, final String sortParam, final String rsqlParam) {
|
||||
final int sanitizedOffsetParam = PagingUtility.sanitizeOffsetParam(pagingOffsetParam);
|
||||
final int sanitizedLimitParam = PagingUtility.sanitizePageLimitParam(pagingLimitParam);
|
||||
final Sort sorting = PagingUtility.sanitizeDistributionSetMetadataSortParam(sortParam);
|
||||
public void createMetadata(final String targetId, final List<MgmtMetadata> metadataRest) {
|
||||
targetManagement.createMetadata(targetId, MgmtTargetMapper.fromRequestMetadata(metadataRest));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
return ResponseEntity.ok(new PagedList<>(MgmtTargetMapper.toResponseTargetMetadata(
|
||||
metaDataPage.getContent()), metaDataPage.getTotalElements()));
|
||||
@Override
|
||||
public ResponseEntity<PagedList<MgmtMetadata>> getMetadata(final String targetId) {
|
||||
final Map<String, String> metadata = targetManagement.getMetadata(targetId);
|
||||
return ResponseEntity.ok(new PagedList<>(MgmtTargetMapper.toResponseMetadata(metadata), metadata.size()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<MgmtMetadata> getMetadataValue(final String targetId, final String metadataKey) {
|
||||
final TargetMetadata findOne = targetManagement.getMetaDataByControllerId(targetId, metadataKey)
|
||||
.orElseThrow(() -> new EntityNotFoundException(TargetMetadata.class, targetId, metadataKey));
|
||||
return ResponseEntity.ok(MgmtTargetMapper.toResponseTargetMetadata(findOne));
|
||||
final String metadataValue = targetManagement.getMetadata(targetId).get(metadataKey);
|
||||
if (metadataValue == null) {
|
||||
throw new EntityNotFoundException("Target metadata", targetId + ":" + metadataKey);
|
||||
}
|
||||
return ResponseEntity.ok(MgmtTargetMapper.toResponseMetadata(metadataKey, metadataValue));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<MgmtMetadata> updateMetadata(final String targetId, final String metadataKey, final MgmtMetadataBodyPut metadata) {
|
||||
final TargetMetadata updated = targetManagement.updateMetadata(targetId,
|
||||
entityFactory.generateTargetMetadata(metadataKey, metadata.getValue()));
|
||||
return ResponseEntity.ok(MgmtTargetMapper.toResponseTargetMetadata(updated));
|
||||
public void updateMetadata(final String targetId, final String metadataKey, final MgmtMetadataBodyPut metadata) {
|
||||
targetManagement.updateMetadata(targetId, metadataKey, metadata.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
@AuditLog(entity = "Target", type = AuditLog.Type.DELETE, description = "Delete Target Metadata")
|
||||
public ResponseEntity<Void> deleteMetadata(final String targetId, final String metadataKey) {
|
||||
targetManagement.deleteMetaData(targetId, metadataKey);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<List<MgmtMetadata>> createMetadata(final String targetId, final List<MgmtMetadata> metadataRest) {
|
||||
final List<TargetMetadata> created = targetManagement.createMetaData(targetId,
|
||||
MgmtTargetMapper.fromRequestTargetMetadata(metadataRest, entityFactory));
|
||||
return new ResponseEntity<>(MgmtTargetMapper.toResponseTargetMetadata(created), HttpStatus.CREATED);
|
||||
public void deleteMetadata(final String targetId, final String metadataKey) {
|
||||
targetManagement.deleteMetadata(targetId, metadataKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -37,7 +37,6 @@ import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -74,12 +73,10 @@ import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.MetaData;
|
||||
import org.eclipse.hawkbit.repository.model.NamedEntity;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.TargetTag;
|
||||
import org.eclipse.hawkbit.repository.model.TargetType;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
@@ -114,8 +111,6 @@ import org.springframework.test.web.servlet.ResultActions;
|
||||
@Story("Target Resource")
|
||||
class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
ActionRepository actionRepository;
|
||||
private static final String TARGET_DESCRIPTION_TEST = "created in test";
|
||||
private static final String JSON_PATH_ROOT = "$";
|
||||
// fields, attributes
|
||||
@@ -139,23 +134,26 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
|
||||
private static final String JSON_PATH_DESCRIPTION = JSON_PATH_ROOT + JSON_PATH_FIELD_DESCRIPTION;
|
||||
private static final String JSON_PATH_LAST_REQUEST_AT = JSON_PATH_ROOT + JSON_PATH_FIELD_LAST_REQUEST_AT;
|
||||
private static final String JSON_PATH_TYPE = JSON_PATH_ROOT + JSON_PATH_FIELD_TARGET_TYPE;
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
ActionRepository actionRepository;
|
||||
@Autowired
|
||||
private JpaProperties jpaProperties;
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@Test
|
||||
@Description("Ensures that when targetType value of -1 is provided the target type is unassigned from the target.")
|
||||
public void updateTargetAndUnnasignTargetType() throws Exception {
|
||||
void updateTargetAndUnassignTargetType() throws Exception {
|
||||
final String knownControllerId = "123";
|
||||
final String knownNewAddress = "amqp://test123/foobar";
|
||||
final String knownNameNotModify = "controllerName";
|
||||
final Long unnasignTargetTypeValue = -1L;
|
||||
final Long unassignTargetTypeValue = -1L;
|
||||
|
||||
final TargetType targetType = targetTypeManagement.create(
|
||||
entityFactory.targetType().create().name("targettype1").description("targettypedes1"));
|
||||
|
||||
final String body = new JSONObject().put("targetType", unnasignTargetTypeValue).toString();
|
||||
final String body = new JSONObject().put("targetType", unassignTargetTypeValue).toString();
|
||||
|
||||
// create a target with the created TargetType
|
||||
targetManagement.create(entityFactory.target().create().controllerId(knownControllerId).name(knownNameNotModify)
|
||||
@@ -183,18 +181,18 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
|
||||
|
||||
@Test
|
||||
@Description("Ensures that when targetType value of -1 is provided the target type is unassigned from the target when updating multiple fields in target object.")
|
||||
public void updateTargetNameAndUnnasignTargetType() throws Exception {
|
||||
void updateTargetNameAndUnassignTargetType() throws Exception {
|
||||
final String knownControllerId = "123";
|
||||
final String knownNewAddress = "amqp://test123/foobar";
|
||||
final String knownNameNotModify = "controllerName";
|
||||
final Long unnasignTargetTypeValue = -1L;
|
||||
final Long unassignTargetTypeValue = -1L;
|
||||
final String controllerNewName = "controllerNewName";
|
||||
|
||||
final TargetType targetType = targetTypeManagement.create(
|
||||
entityFactory.targetType().create().name("targettype1").description("targettypedes1"));
|
||||
|
||||
final String body = new JSONObject()
|
||||
.put("targetType", unnasignTargetTypeValue).put("name", "controllerNewName")
|
||||
.put("targetType", unassignTargetTypeValue).put("name", "controllerNewName")
|
||||
.toString();
|
||||
|
||||
// create a target with the created TargetType
|
||||
@@ -223,7 +221,7 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
|
||||
|
||||
@Test
|
||||
@Description("Handles the GET request of retrieving all targets within SP..")
|
||||
public void getTargets() throws Exception {
|
||||
void getTargets() throws Exception {
|
||||
enableConfirmationFlow();
|
||||
mvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING))
|
||||
.andExpect(status().isOk())
|
||||
@@ -232,33 +230,15 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
|
||||
|
||||
@Test
|
||||
@Description("Handles the GET request of retrieving all targets within SP based by parameter.")
|
||||
public void getTargetsWithParameters() throws Exception {
|
||||
void getTargetsWithParameters() throws Exception {
|
||||
mvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "?limit=10&sort=name:ASC&offset=0&q=name==a"))
|
||||
.andExpect(status().isOk())
|
||||
.andDo(MockMvcResultPrinter.print());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Get a paged list of meta data for a target with standard page size.")
|
||||
public void getMetadata() throws Exception {
|
||||
final int totalMetadata = 4;
|
||||
final String knownKeyPrefix = "knownKey";
|
||||
final String knownValuePrefix = "knownValue";
|
||||
final Target testTarget = testdataFactory.createTarget("targetId");
|
||||
for (int index = 0; index < totalMetadata; index++) {
|
||||
targetManagement.createMetaData(testTarget.getControllerId(), List.of(
|
||||
entityFactory.generateTargetMetadata(knownKeyPrefix + index, knownValuePrefix + index)));
|
||||
}
|
||||
|
||||
mvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/metadata", testTarget.getControllerId()))
|
||||
.andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().contentType(MediaTypes.HAL_JSON));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Handles the POST request to activate auto-confirm on a target. Payload can be provided to specify more details about the operation.")
|
||||
public void postActivateAutoConfirm() throws Exception {
|
||||
void postActivateAutoConfirm() throws Exception {
|
||||
final Target testTarget = testdataFactory.createTarget("targetId");
|
||||
|
||||
final MgmtTargetAutoConfirmUpdate body = new MgmtTargetAutoConfirmUpdate("custom_initiator_value",
|
||||
@@ -273,7 +253,7 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
|
||||
|
||||
@Test
|
||||
@Description("Handles the POST request to deactivate auto-confirm on a target.")
|
||||
public void postDeactivateAutoConfirm() throws Exception {
|
||||
void postDeactivateAutoConfirm() throws Exception {
|
||||
final Target testTarget = testdataFactory.createTarget("targetId");
|
||||
confirmationManagement.activateAutoConfirmation(testTarget.getControllerId(), null, null);
|
||||
|
||||
@@ -285,7 +265,7 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
|
||||
|
||||
@Test
|
||||
@Description("Test confirmation of single Action with confirm status. Check that Action goes into Running status with appropriate messages and status code")
|
||||
public void updateActionConfirmationWithConfirm() throws Exception {
|
||||
void updateActionConfirmationWithConfirm() throws Exception {
|
||||
final int expectedStatusCode = 210;
|
||||
final String expectedStatusMessage1 = "some-custom-message1";
|
||||
final String expectedStatusMessage2 = "some-custom-message2";
|
||||
@@ -296,7 +276,7 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
|
||||
|
||||
@Test
|
||||
@Description("Test confirmation of single Action with deny status. Check that Action stays in WAIT_FOR_CONFIRMATION status with appropriate messages and status code")
|
||||
public void updateActionConfirmationWithDeny() throws Exception {
|
||||
void updateActionConfirmationWithDeny() throws Exception {
|
||||
final int expectedStatusCode = 410;
|
||||
final String expectedStatusMessage1 = "some-error-custom-message1";
|
||||
final String expectedStatusMessage2 = "some-error-custom-message2";
|
||||
@@ -307,7 +287,7 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
|
||||
|
||||
@Test
|
||||
@Description("Test confirmation of single Action with wrong ControllerId - e.g. the given Action is not assigned to the given Target - confirmation call must fail.")
|
||||
public void updateActionConfirmationFailsIfActionNotAssignedToTarget() throws Exception {
|
||||
void updateActionConfirmationFailsIfActionNotAssignedToTarget() throws Exception {
|
||||
final int payloadCallCode = 200;
|
||||
final String payloadCallMessage1 = "random1";
|
||||
final String payloadCallMessage2 = "random2";
|
||||
@@ -2005,17 +1985,10 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
|
||||
.contentType(MediaType.APPLICATION_JSON).content(metaData1.toString()))
|
||||
.andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isCreated())
|
||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
|
||||
.andExpect(jsonPath("[0].key", equalTo(knownKey1)))
|
||||
.andExpect(jsonPath("[0].value", equalTo(knownValue1)))
|
||||
.andExpect(jsonPath("[1].key", equalTo(knownKey2)))
|
||||
.andExpect(jsonPath("[1].value", equalTo(knownValue2)));
|
||||
.andExpect(content().string(""));
|
||||
|
||||
final TargetMetadata metaKey1 = targetManagement.getMetaDataByControllerId(knownControllerId, knownKey1).get();
|
||||
final TargetMetadata metaKey2 = targetManagement.getMetaDataByControllerId(knownControllerId, knownKey2).get();
|
||||
|
||||
assertThat(metaKey1.getValue()).isEqualTo(knownValue1);
|
||||
assertThat(metaKey2.getValue()).isEqualTo(knownValue2);
|
||||
assertThat(targetManagement.getMetadata(knownControllerId).get(knownKey1)).isEqualTo(knownValue1);
|
||||
assertThat(targetManagement.getMetadata(knownControllerId).get(knownKey2)).isEqualTo(knownValue2);
|
||||
|
||||
// verify quota enforcement
|
||||
final int maxMetaData = quotaManagement.getMaxMetaDataEntriesPerTarget();
|
||||
@@ -2025,16 +1998,14 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
|
||||
metaData2.put(new JSONObject().put("key", knownKey1 + i).put("value", knownValue1 + i));
|
||||
}
|
||||
|
||||
mvc.perform(post("/rest/v1/targets/{targetId}/metadata", knownControllerId).accept(MediaType.APPLICATION_JSON)
|
||||
mvc.perform(post("/rest/v1/targets/{targetId}/metadata", knownControllerId)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.contentType(MediaType.APPLICATION_JSON).content(metaData2.toString()))
|
||||
.andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isForbidden());
|
||||
|
||||
// verify that the number of meta data entries has not changed
|
||||
// (we cannot use the PAGE constant here as it tries to sort by ID)
|
||||
assertThat(targetManagement.findMetaDataByControllerId(PageRequest.of(0, Integer.MAX_VALUE), knownControllerId)
|
||||
.getTotalElements()).isEqualTo(metaData1.length());
|
||||
|
||||
// verify that the number of meta-data entries has not changed (we cannot use the PAGE constant here as it tries to sort by ID)
|
||||
assertThat(targetManagement.getMetadata(knownControllerId)).hasSize(metaData1.length());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -2056,14 +2027,9 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
|
||||
.content(jsonObject.toString()))
|
||||
.andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
|
||||
.andExpect(jsonPath("key", equalTo(knownKey)))
|
||||
.andExpect(jsonPath("value", equalTo(updateValue)));
|
||||
|
||||
final TargetMetadata updatedTargetMetadata = targetManagement
|
||||
.getMetaDataByControllerId(knownControllerId, knownKey).get();
|
||||
assertThat(updatedTargetMetadata.getValue()).isEqualTo(updateValue);
|
||||
.andExpect(content().string(""));
|
||||
|
||||
assertThat(targetManagement.getMetadata(knownControllerId).get(knownKey)).isEqualTo(updateValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -2081,7 +2047,12 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
|
||||
.andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isOk());
|
||||
|
||||
assertThat(targetManagement.getMetaDataByControllerId(knownControllerId, knownKey)).isNotPresent();
|
||||
// already deleted
|
||||
mvc.perform(delete("/rest/v1/targets/{targetId}/metadata/{key}", knownControllerId, knownKey))
|
||||
.andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
assertThat(targetManagement.getMetadata(knownControllerId).get(knownKey)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -2103,7 +2074,7 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
|
||||
.andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
assertThat(targetManagement.getMetaDataByControllerId(knownControllerId, knownKey)).isPresent();
|
||||
assertThat(targetManagement.getMetadata(knownControllerId).get(knownKey)).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -2126,53 +2097,27 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
|
||||
|
||||
@Test
|
||||
@Description("Ensures that a metadata entry paged list selection through API reflectes the repository content.")
|
||||
void getPagedListOfMetadata() throws Exception {
|
||||
void getMetadata() throws Exception {
|
||||
final String knownControllerId = "targetIdWithMetadata";
|
||||
|
||||
final int totalMetadata = 10;
|
||||
final int limitParam = 5;
|
||||
final String offsetParam = "0";
|
||||
final String knownKeyPrefix = "knownKey";
|
||||
final String knownValuePrefix = "knownValue";
|
||||
|
||||
setupTargetWithMetadata(knownControllerId, knownKeyPrefix, knownValuePrefix, totalMetadata);
|
||||
|
||||
mvc.perform(get("/rest/v1/targets/{targetId}/metadata?offset=" + offsetParam + "&limit=" + limitParam,
|
||||
knownControllerId))
|
||||
mvc.perform(get("/rest/v1/targets/{targetId}/metadata", knownControllerId))
|
||||
.andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("size", equalTo(limitParam)))
|
||||
.andExpect(jsonPath("size", equalTo(totalMetadata)))
|
||||
.andExpect(jsonPath("total", equalTo(totalMetadata)))
|
||||
.andExpect(jsonPath("content[0].key", equalTo("knownKey0")))
|
||||
.andExpect(jsonPath("content[0].value", equalTo("knownValue0")));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Ensures that a target metadata filtered query with value==knownValue1 parameter returns only the metadata entries with that value.")
|
||||
void searchDistributionSetMetadataRsql() throws Exception {
|
||||
final String knownControllerId = "targetIdWithMetadata";
|
||||
|
||||
final int totalMetadata = 10;
|
||||
final String knownKeyPrefix = "knownKey";
|
||||
final String knownValuePrefix = "knownValue";
|
||||
|
||||
setupTargetWithMetadata(knownControllerId, knownKeyPrefix, knownValuePrefix, totalMetadata);
|
||||
|
||||
final String rsqlSearchValue1 = "value==knownValue1";
|
||||
|
||||
mvc.perform(get("/rest/v1/targets/{targetId}/metadata?q=" + rsqlSearchValue1, knownControllerId))
|
||||
.andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("size", equalTo(1)))
|
||||
.andExpect(jsonPath("total", equalTo(1)))
|
||||
.andExpect(jsonPath("content[0].key", equalTo("knownKey1")))
|
||||
.andExpect(jsonPath("content[0].value", equalTo("knownValue1")));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("A request for assigning multiple DS to a target results in a Bad Request when multiassignment in disabled.")
|
||||
void multiassignmentRequestNotAllowedIfDisabled() throws Exception {
|
||||
void multiAssignmentRequestNotAllowedIfDisabled() throws Exception {
|
||||
final String targetId = testdataFactory.createTarget().getControllerId();
|
||||
final List<Long> dsIds = testdataFactory.createDistributionSets(2).stream().map(DistributionSet::getId)
|
||||
.toList();
|
||||
@@ -2953,23 +2898,20 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
|
||||
return targetManagement.getByControllerID(tA.getControllerId()).get();
|
||||
}
|
||||
|
||||
private void setupTargetWithMetadata(final String knownControllerId, final String knownKey,
|
||||
final String knownValue) {
|
||||
private void setupTargetWithMetadata(final String knownControllerId, final String knownKey, final String knownValue) {
|
||||
testdataFactory.createTarget(knownControllerId);
|
||||
targetManagement.createMetaData(knownControllerId,
|
||||
Collections.singletonList(entityFactory.generateTargetMetadata(knownKey, knownValue)));
|
||||
targetManagement.createMetadata(knownControllerId, Map.of(knownKey, knownValue));
|
||||
}
|
||||
|
||||
private void setupTargetWithMetadata(final String knownControllerId, final String knownKeyPrefix,
|
||||
final String knownValuePrefix, final int totalMetadata) {
|
||||
private void setupTargetWithMetadata(
|
||||
final String knownControllerId, final String knownKeyPrefix, final String knownValuePrefix, final int totalMetadata) {
|
||||
testdataFactory.createTarget(knownControllerId);
|
||||
|
||||
final List<MetaData> targetMetadataEntries = new LinkedList<>();
|
||||
final Map<String, String> metadataEntries = new HashMap<>();
|
||||
for (int index = 0; index < totalMetadata; index++) {
|
||||
targetMetadataEntries
|
||||
.add(entityFactory.generateTargetMetadata(knownKeyPrefix + index, knownValuePrefix + index));
|
||||
metadataEntries.put(knownKeyPrefix + index, knownValuePrefix + index);
|
||||
}
|
||||
targetManagement.createMetaData(knownControllerId, targetMetadataEntries);
|
||||
targetManagement.createMetadata(knownControllerId, metadataEntries);
|
||||
}
|
||||
|
||||
private Action updateActionStatus(final Action action, final Status status, final Integer statusCode) {
|
||||
|
||||
Reference in New Issue
Block a user