Add new endpoint for single action (#1316)
* Add new endpoint for single action * Adding the new endpoint to the documentation + reverse the representation mode to FULL Signed-off-by: Stanislav Trailov <stanislav.trailov@bosch.io>
This commit is contained in:
committed by
GitHub
parent
a64c2bc28e
commit
64bc0417b1
@@ -14,6 +14,7 @@ import org.springframework.hateoas.MediaTypes;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
@@ -54,4 +55,14 @@ public interface MgmtActionRestApi {
|
||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) String rsqlParam,
|
||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_REPRESENTATION_MODE, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_REPRESENTATION_MODE_DEFAULT) String representationModeParam);
|
||||
|
||||
/**
|
||||
* Handles the GET request of retrieving a specific Action by <code>actionId</code>
|
||||
*
|
||||
* @param actionId
|
||||
*
|
||||
* @return the action
|
||||
*/
|
||||
@GetMapping(value = "/{actionId}", produces = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
|
||||
ResponseEntity<MgmtAction> getAction(
|
||||
@PathVariable("actionId") Long actionId);
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import org.eclipse.hawkbit.mgmt.rest.api.MgmtActionRestApi;
|
||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRepresentationMode;
|
||||
import org.eclipse.hawkbit.repository.DeploymentManagement;
|
||||
import org.eclipse.hawkbit.repository.OffsetBasedPageRequest;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -55,16 +56,29 @@ public class MgmtActionResource implements MgmtActionRestApi {
|
||||
totalActionCount = this.deploymentManagement.countActionsAll();
|
||||
}
|
||||
|
||||
final MgmtRepresentationMode repMode = MgmtRepresentationMode.fromValue(representationModeParam)
|
||||
.orElseGet(() -> {
|
||||
// no need for a 400, just apply a safe fallback
|
||||
LOG.warn("Received an invalid representation mode: {}", representationModeParam);
|
||||
return MgmtRepresentationMode.COMPACT;
|
||||
});
|
||||
final MgmtRepresentationMode repMode = getRepresentationModeFromString(representationModeParam);
|
||||
|
||||
return ResponseEntity
|
||||
.ok(new PagedList<>(MgmtActionMapper.toResponse(actions.getContent(), repMode), totalActionCount));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<MgmtAction> getAction(final Long actionId) {
|
||||
|
||||
final Action action = deploymentManagement.findAction(actionId)
|
||||
.orElseThrow(() -> new EntityNotFoundException(Action.class, actionId));
|
||||
|
||||
return ResponseEntity.ok(MgmtActionMapper.toResponse(action, MgmtRepresentationMode.FULL));
|
||||
}
|
||||
|
||||
private MgmtRepresentationMode getRepresentationModeFromString(final String representationModeParam) {
|
||||
return MgmtRepresentationMode.fromValue(representationModeParam)
|
||||
.orElseGet(() -> {
|
||||
// no need for a 400, just apply a safe fallback
|
||||
LOG.warn("Received an invalid representation mode: {}", representationModeParam);
|
||||
return MgmtRepresentationMode.COMPACT;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -54,10 +54,14 @@ class MgmtActionResourceTest extends AbstractManagementApiIntegrationTest {
|
||||
private static final String JSON_PATH_FIELD_SIZE = ".size";
|
||||
private static final String JSON_PATH_FIELD_TOTAL = ".total";
|
||||
|
||||
private static final String JSON_PATH_FIELD_ID = ".id";
|
||||
|
||||
private static final String JSON_PATH_PAGED_LIST_CONTENT = JSON_PATH_ROOT + JSON_PATH_FIELD_CONTENT;
|
||||
private static final String JSON_PATH_PAGED_LIST_SIZE = JSON_PATH_ROOT + JSON_PATH_FIELD_SIZE;
|
||||
private static final String JSON_PATH_PAGED_LIST_TOTAL = JSON_PATH_ROOT + JSON_PATH_FIELD_TOTAL;
|
||||
|
||||
private static final String JSON_PATH_ACTION_ID = JSON_PATH_ROOT + JSON_PATH_FIELD_ID;
|
||||
|
||||
@Test
|
||||
@Description("Verifies that actions can be filtered based on action status.")
|
||||
void filterActionsByStatus() throws Exception {
|
||||
@@ -381,10 +385,6 @@ class MgmtActionResourceTest extends AbstractManagementApiIntegrationTest {
|
||||
final List<Action> actions = generateTargetWithTwoUpdatesWithOneOverride(knownTargetId);
|
||||
final Long actionId = actions.get(0).getId();
|
||||
|
||||
// ensure specific action cannot be accessed via the actions resource
|
||||
mvc.perform(get(MgmtRestConstants.ACTION_V1_REQUEST_MAPPING + "/" + actionId))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
|
||||
|
||||
// not allowed methods
|
||||
mvc.perform(post(MgmtRestConstants.ACTION_V1_REQUEST_MAPPING)).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isMethodNotAllowed());
|
||||
@@ -394,6 +394,28 @@ class MgmtActionResourceTest extends AbstractManagementApiIntegrationTest {
|
||||
.andExpect(status().isMethodNotAllowed());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verifies that the correct action is returned")
|
||||
void shouldRetrieveCorrectActionById() throws Exception {
|
||||
final String knownTargetId = "targetId";
|
||||
|
||||
final List<Action> actions = generateTargetWithTwoUpdatesWithOneOverride(knownTargetId);
|
||||
final Long actionId = actions.get(0).getId();
|
||||
|
||||
mvc.perform(get(MgmtRestConstants.ACTION_V1_REQUEST_MAPPING + "/" + actionId))
|
||||
.andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath(JSON_PATH_ACTION_ID, equalTo(actionId.intValue())));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verifies that NOT_FOUND is returned when there is no such action.")
|
||||
void requestActionThatDoesNotExistsLeadsToNotFound() throws Exception {
|
||||
mvc.perform(get(MgmtRestConstants.ACTION_V1_REQUEST_MAPPING + "/" + 101))
|
||||
.andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isNotFound());
|
||||
}
|
||||
|
||||
private List<Action> generateTargetWithTwoUpdatesWithOneOverride(final String knownTargetId) {
|
||||
return generateTargetWithTwoUpdatesWithOneOverrideWithMaintenanceWindow(knownTargetId, null, null, null);
|
||||
}
|
||||
|
||||
@@ -65,6 +65,44 @@ include::../errors/406.adoc[]
|
||||
include::../errors/429.adoc[]
|
||||
|===
|
||||
|
||||
== GET /rest/v1/actions/{actionId}
|
||||
|
||||
=== Implementation notes
|
||||
|
||||
Handles the GET request of retrieving a single action within Hawkbit by actionId.
|
||||
|
||||
=== Get single action
|
||||
|
||||
==== CURL
|
||||
|
||||
include::{snippets}/actions/get-action/curl-request.adoc[]
|
||||
|
||||
==== Request URL
|
||||
|
||||
A `GET` request is used to access a single action
|
||||
|
||||
include::{snippets}/actions/get-action/http-request.adoc[]
|
||||
|
||||
=== Response (Status 200)
|
||||
|
||||
==== Response fields
|
||||
|
||||
include::{snippets}/actions/get-action/response-fields.adoc[]
|
||||
|
||||
==== Response example
|
||||
|
||||
include::{snippets}/actions/get-action/http-response.adoc[]
|
||||
|
||||
|===
|
||||
| HTTP Status Code | Reason | Response Model
|
||||
|
||||
include::../errors/400.adoc[]
|
||||
include::../errors/401.adoc[]
|
||||
include::../errors/404.adoc[]
|
||||
include::../errors/406.adoc[]
|
||||
include::../errors/429.adoc[]
|
||||
|===
|
||||
|
||||
== Additional content
|
||||
|
||||
[[error-body]]
|
||||
|
||||
@@ -11,8 +11,7 @@ package org.eclipse.hawkbit.rest.mgmt.documentation;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
|
||||
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
|
||||
import static org.springframework.restdocs.request.RequestDocumentation.requestParameters;
|
||||
import static org.springframework.restdocs.request.RequestDocumentation.*;
|
||||
import static org.springframework.restdocs.snippet.Attributes.key;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@@ -103,6 +102,47 @@ public class ActionResourceDocumentationTest extends AbstractApiRestDocumentatio
|
||||
parameterWithName("representation").description(MgmtApiModelProperties.REPRESENTATION_MODE))));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Handles the GET request of retrieving a specific action.")
|
||||
public void getAction() throws Exception {
|
||||
final Action action = generateRolloutActionForTarget(targetId);
|
||||
provideCodeFeedback(action, 200);
|
||||
|
||||
assertThat(deploymentManagement.findAction(action.getId()).get().getActionType())
|
||||
.isEqualTo(Action.ActionType.FORCED);
|
||||
|
||||
mockMvc.perform(get(MgmtRestConstants.ACTION_V1_REQUEST_MAPPING + "/{actionId}", action.getId()))
|
||||
.andExpect(status().isOk()).andDo(MockMvcResultPrinter.print())
|
||||
.andDo(this.document.document(
|
||||
pathParameters(parameterWithName("actionId").description(ApiModelPropertiesGeneric.ITEM_ID)),
|
||||
responseFields(fieldWithPath("createdBy").description(ApiModelPropertiesGeneric.CREATED_BY),
|
||||
fieldWithPath("createdAt").description(ApiModelPropertiesGeneric.CREATED_AT),
|
||||
fieldWithPath("id").description(MgmtApiModelProperties.ACTION_ID),
|
||||
fieldWithPath("lastModifiedBy").description(ApiModelPropertiesGeneric.LAST_MODIFIED_BY)
|
||||
.type("String"),
|
||||
fieldWithPath("lastModifiedAt").description(ApiModelPropertiesGeneric.LAST_MODIFIED_AT)
|
||||
.type("String"),
|
||||
fieldWithPath("type").description(MgmtApiModelProperties.ACTION_TYPE)
|
||||
.attributes(key("value").value("['update', 'cancel']")),
|
||||
fieldWithPath("forceType").description(MgmtApiModelProperties.ACTION_FORCE_TYPE)
|
||||
.attributes(key("value").value("['forced', 'soft', 'timeforced']")),
|
||||
fieldWithPath("status").description(MgmtApiModelProperties.ACTION_EXECUTION_STATUS)
|
||||
.attributes(key("value").value("['finished', 'pending']")),
|
||||
fieldWithPath("detailStatus").description(MgmtApiModelProperties.ACTION_DETAIL_STATUS)
|
||||
.attributes(key("value").value(
|
||||
"['finished', 'error', 'running', 'warning', 'scheduled', 'canceling', 'canceled', 'download', 'downloaded', 'retrieved', 'cancel_rejected']")),
|
||||
optionalRequestFieldWithPath("lastStatusCode")
|
||||
.description(MgmtApiModelProperties.ACTION_LAST_STATUS_CODE).type("Integer"),
|
||||
fieldWithPath("rollout").description(MgmtApiModelProperties.ACTION_ROLLOUT),
|
||||
fieldWithPath("rolloutName").description(MgmtApiModelProperties.ACTION_ROLLOUT_NAME),
|
||||
fieldWithPath("_links.self").ignored(),
|
||||
fieldWithPath("_links.distributionset").description(MgmtApiModelProperties.LINK_TO_DS),
|
||||
fieldWithPath("_links.status")
|
||||
.description(MgmtApiModelProperties.LINKS_ACTION_STATUSES),
|
||||
fieldWithPath("_links.rollout").description(MgmtApiModelProperties.LINK_TO_ROLLOUT),
|
||||
fieldWithPath("_links.target").description(MgmtApiModelProperties.LINK_TO_TARGET))));
|
||||
}
|
||||
|
||||
private Action generateRolloutActionForTarget(final String knownControllerId) throws Exception {
|
||||
return generateActionForTarget(knownControllerId, true, false, null, null, null, true);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user