Add support for target tag retrieval via REST (#1782)
Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -13,6 +13,7 @@ import java.util.Collection;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import jakarta.validation.ConstraintViolationException;
|
import jakarta.validation.ConstraintViolationException;
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
@@ -851,6 +852,16 @@ public interface TargetManagement {
|
|||||||
boolean isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(@NotNull String controllerId,
|
boolean isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(@NotNull String controllerId,
|
||||||
long distributionSetId, @NotNull String targetFilterQuery);
|
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.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import jakarta.persistence.EntityManager;
|
import jakarta.persistence.EntityManager;
|
||||||
@@ -178,6 +179,13 @@ public class JpaTargetManagement implements TargetManagement {
|
|||||||
return targetRepository.count();
|
return targetRepository.count();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<TargetTag> getTagsByControllerId(@NotEmpty String controllerId) {
|
||||||
|
// the method has PreAuthorized by itself
|
||||||
|
return getByControllerID(controllerId).map(JpaTarget.class::cast).map(JpaTarget::getTags)
|
||||||
|
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
@Retryable(include = {
|
@Retryable(include = {
|
||||||
|
|||||||
@@ -207,11 +207,7 @@ public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected Set<TargetTag> getTargetTags(final String controllerId) {
|
protected Set<TargetTag> getTargetTags(final String controllerId) {
|
||||||
return targetRepository
|
return targetManagement.getTagsByControllerId(controllerId);
|
||||||
.findOne(TargetSpecifications.hasControllerId(controllerId))
|
|
||||||
.map(JpaTarget.class::cast)
|
|
||||||
.map(JpaTarget::getTags)
|
|
||||||
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private JpaRollout refresh(final Rollout rollout) {
|
private JpaRollout refresh(final Rollout rollout) {
|
||||||
|
|||||||
@@ -201,7 +201,7 @@ class TargetTagManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SafeVarargs
|
@SafeVarargs
|
||||||
private final <T> Collection<T> concat(final Collection<T>... targets) {
|
private <T> Collection<T> concat(final Collection<T>... targets) {
|
||||||
final List<T> result = new ArrayList<>();
|
final List<T> result = new ArrayList<>();
|
||||||
Arrays.asList(targets).forEach(result::addAll);
|
Arrays.asList(targets).forEach(result::addAll);
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import org.eclipse.hawkbit.mgmt.json.model.action.MgmtActionRequestBodyPut;
|
|||||||
import org.eclipse.hawkbit.mgmt.json.model.action.MgmtActionStatus;
|
import org.eclipse.hawkbit.mgmt.json.model.action.MgmtActionStatus;
|
||||||
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSet;
|
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSet;
|
||||||
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtTargetAssignmentResponseBody;
|
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtTargetAssignmentResponseBody;
|
||||||
|
import org.eclipse.hawkbit.mgmt.json.model.tag.MgmtTag;
|
||||||
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtDistributionSetAssignments;
|
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtDistributionSetAssignments;
|
||||||
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTarget;
|
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTarget;
|
||||||
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTargetAttributes;
|
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTargetAttributes;
|
||||||
@@ -715,6 +716,34 @@ public interface MgmtTargetRestApi {
|
|||||||
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
|
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
|
||||||
ResponseEntity<MgmtDistributionSet> getInstalledDistributionSet(@PathVariable("targetId") String targetId);
|
ResponseEntity<MgmtDistributionSet> getInstalledDistributionSet(@PathVariable("targetId") String targetId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a paged list of meta data for a target.
|
||||||
|
*
|
||||||
|
* @param targetId the ID of the target for the meta data
|
||||||
|
*/
|
||||||
|
@Operation(summary = "Return tags for specific target", description = "Get a paged list of tags for a target. Required permission: READ_REPOSITORY")
|
||||||
|
@ApiResponses(value = {
|
||||||
|
@ApiResponse(responseCode = "200", description = "Successfully retrieved"),
|
||||||
|
@ApiResponse(responseCode = "204", description = "No tags"),
|
||||||
|
@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 = "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)))
|
||||||
|
})
|
||||||
|
@GetMapping(value = MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/tags", produces = {
|
||||||
|
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
|
||||||
|
ResponseEntity<List<MgmtTag>> getTags(@PathVariable("targetId") String targetId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a paged list of meta data for a target.
|
* Gets a paged list of meta data for a target.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ import java.util.Collection;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
import org.eclipse.hawkbit.mgmt.json.model.tag.MgmtTag;
|
import org.eclipse.hawkbit.mgmt.json.model.tag.MgmtTag;
|
||||||
import org.eclipse.hawkbit.mgmt.json.model.tag.MgmtTagRequestBodyPut;
|
import org.eclipse.hawkbit.mgmt.json.model.tag.MgmtTagRequestBodyPut;
|
||||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtDistributionSetTagRestApi;
|
import org.eclipse.hawkbit.mgmt.rest.api.MgmtDistributionSetTagRestApi;
|
||||||
@@ -30,14 +32,10 @@ import org.eclipse.hawkbit.repository.model.TargetTag;
|
|||||||
import org.eclipse.hawkbit.rest.data.ResponseList;
|
import org.eclipse.hawkbit.rest.data.ResponseList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A mapper which maps repository model to RESTful model representation and
|
* A mapper which maps repository model to RESTful model representation and back.
|
||||||
* back.
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
final class MgmtTagMapper {
|
final class MgmtTagMapper {
|
||||||
private MgmtTagMapper() {
|
|
||||||
// Utility class
|
|
||||||
}
|
|
||||||
|
|
||||||
static List<MgmtTag> toResponse(final List<TargetTag> targetTags) {
|
static List<MgmtTag> toResponse(final List<TargetTag> targetTags) {
|
||||||
final List<MgmtTag> tagsRest = new ArrayList<>();
|
final List<MgmtTag> tagsRest = new ArrayList<>();
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import java.util.Collection;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@@ -30,6 +31,7 @@ import org.eclipse.hawkbit.mgmt.json.model.action.MgmtActionStatus;
|
|||||||
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType;
|
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType;
|
||||||
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSet;
|
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSet;
|
||||||
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtTargetAssignmentResponseBody;
|
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtTargetAssignmentResponseBody;
|
||||||
|
import org.eclipse.hawkbit.mgmt.json.model.tag.MgmtTag;
|
||||||
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtDistributionSetAssignments;
|
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtDistributionSetAssignments;
|
||||||
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTarget;
|
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTarget;
|
||||||
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTargetAttributes;
|
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTargetAttributes;
|
||||||
@@ -51,6 +53,7 @@ import org.eclipse.hawkbit.repository.model.DeploymentRequest;
|
|||||||
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
|
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
|
||||||
import org.eclipse.hawkbit.repository.model.Target;
|
import org.eclipse.hawkbit.repository.model.Target;
|
||||||
import org.eclipse.hawkbit.repository.model.TargetMetadata;
|
import org.eclipse.hawkbit.repository.model.TargetMetadata;
|
||||||
|
import org.eclipse.hawkbit.repository.model.TargetTag;
|
||||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||||
import org.eclipse.hawkbit.utils.TenantConfigHelper;
|
import org.eclipse.hawkbit.utils.TenantConfigHelper;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
@@ -391,6 +394,16 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
|
|||||||
return ResponseEntity.ok(MgmtTargetMapper.toResponseWithLinks(targetId, action));
|
return ResponseEntity.ok(MgmtTargetMapper.toResponseWithLinks(targetId, action));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseEntity<List<MgmtTag>> getTags(@PathVariable("targetId") String targetId) {
|
||||||
|
final Set<TargetTag> tags = targetManagement.getTagsByControllerId(targetId);
|
||||||
|
if (tags.isEmpty()) {
|
||||||
|
return ResponseEntity.noContent().build();
|
||||||
|
} else {
|
||||||
|
return ResponseEntity.ok(MgmtTagMapper.toResponse(tags.stream().toList()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ResponseEntity<PagedList<MgmtMetadata>> getMetadata(@PathVariable("targetId") final String targetId,
|
public ResponseEntity<PagedList<MgmtMetadata>> getMetadata(@PathVariable("targetId") final String targetId,
|
||||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) final int pagingOffsetParam,
|
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) final int pagingOffsetParam,
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import static org.hamcrest.Matchers.contains;
|
|||||||
import static org.hamcrest.Matchers.hasItem;
|
import static org.hamcrest.Matchers.hasItem;
|
||||||
import static org.hamcrest.Matchers.hasKey;
|
import static org.hamcrest.Matchers.hasKey;
|
||||||
import static org.hamcrest.Matchers.hasSize;
|
import static org.hamcrest.Matchers.hasSize;
|
||||||
|
import static org.hamcrest.Matchers.in;
|
||||||
import static org.hamcrest.Matchers.notNullValue;
|
import static org.hamcrest.Matchers.notNullValue;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
@@ -70,6 +71,7 @@ import org.eclipse.hawkbit.repository.model.Rollout;
|
|||||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||||
import org.eclipse.hawkbit.repository.model.Target;
|
import org.eclipse.hawkbit.repository.model.Target;
|
||||||
import org.eclipse.hawkbit.repository.model.TargetMetadata;
|
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.TargetType;
|
||||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||||
import org.eclipse.hawkbit.repository.test.util.WithUser;
|
import org.eclipse.hawkbit.repository.test.util.WithUser;
|
||||||
@@ -1895,6 +1897,39 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
|
|||||||
return targetManagement.getByControllerID(tA.getControllerId()).get();
|
return targetManagement.getByControllerID(tA.getControllerId()).get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getControllerTagReturnsTagWithNoContent() throws Exception {
|
||||||
|
// create target with attributes
|
||||||
|
final String knownTargetId = "targetIdWithNoTags";
|
||||||
|
final Target target = testdataFactory.createTarget(knownTargetId);
|
||||||
|
|
||||||
|
// test query target over rest resource
|
||||||
|
mvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId + "/tags"))
|
||||||
|
.andDo(MockMvcResultPrinter.print())
|
||||||
|
.andExpect(status().isNoContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getControllerTagReturnsTagWithOk() throws Exception {
|
||||||
|
// create target with attributes
|
||||||
|
final String knownTargetId = "targetIdWithTags";
|
||||||
|
final Target target = testdataFactory.createTarget(knownTargetId);
|
||||||
|
final List<TargetTag> targetTags = testdataFactory.createTargetTags(2, "tag_getControllerTagReturnsTagWithOk");
|
||||||
|
final List<String> tagNames = new ArrayList<>();
|
||||||
|
for (final TargetTag targetTag : targetTags) {
|
||||||
|
targetManagement.toggleTagAssignment(Collections.singletonList(target.getControllerId()), targetTag.getName());
|
||||||
|
tagNames.add(targetTag.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
// test query target over rest resource
|
||||||
|
mvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId + "/tags"))
|
||||||
|
.andDo(MockMvcResultPrinter.print())
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$", Matchers.hasSize(2)))
|
||||||
|
.andExpect(jsonPath("$.[0].name", in(tagNames)))
|
||||||
|
.andExpect(jsonPath("$.[1].name", in(tagNames)));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Ensures that the metadata creation through API is reflected by the repository.")
|
@Description("Ensures that the metadata creation through API is reflected by the repository.")
|
||||||
void createMetadata() throws Exception {
|
void createMetadata() throws Exception {
|
||||||
|
|||||||
Reference in New Issue
Block a user