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

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

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

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