diff --git a/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/action/MgmtAction.java b/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/action/MgmtAction.java index 5a87269ce..a86b471b8 100644 --- a/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/action/MgmtAction.java +++ b/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/action/MgmtAction.java @@ -116,4 +116,7 @@ public class MgmtAction extends MgmtBaseEntity { @Schema(description = "(Optional) Code provided as part of the last status update that was sent by the device.", example = "200") private Integer lastStatusCode; + @JsonProperty + @Schema(description = "If created by external system this field contains the external reference for the action") + private String externalRef; } \ No newline at end of file diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtActionMapper.java b/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtActionMapper.java index a71db9921..1df95a931 100644 --- a/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtActionMapper.java +++ b/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtActionMapper.java @@ -42,7 +42,8 @@ public final class MgmtActionMapper { if (actions == null) { return Collections.emptyList(); } - return new ResponseList<>(actions.stream().map(action -> MgmtActionMapper.toResponse(action, repMode)) + return new ResponseList<>(actions.stream() + .map(action -> toResponse(action, repMode)) .collect(Collectors.toList())); } diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetMapper.java b/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetMapper.java index 118198b95..cfe5609e1 100644 --- a/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetMapper.java +++ b/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetMapper.java @@ -53,6 +53,7 @@ import org.eclipse.hawkbit.rest.data.SortDirection; import org.eclipse.hawkbit.util.IpUtil; import org.eclipse.hawkbit.utils.TenantConfigHelper; import org.springframework.data.domain.PageRequest; +import org.springframework.util.ObjectUtils; /** * A mapper which maps repository model to RESTful model representation and @@ -280,6 +281,11 @@ public final class MgmtTargetMapper { result.setMaintenanceWindow(maintenanceWindow); } + final String externalRef = action.getExternalRef(); + if (!ObjectUtils.isEmpty(externalRef)) { + result.setExternalRef(externalRef); + } + MgmtRestModelMapper.mapBaseToBase(result, action); result.add( diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtActionResourceTest.java b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtActionResourceTest.java index a8629328b..918eb1c87 100644 --- a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtActionResourceTest.java +++ b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtActionResourceTest.java @@ -20,6 +20,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import java.time.Duration; +import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -43,6 +44,7 @@ import io.qameta.allure.Description; import io.qameta.allure.Feature; import io.qameta.allure.Step; import io.qameta.allure.Story; +import org.springframework.test.web.servlet.ResultActions; /** * Integration test for the {@link MgmtActionRestApi}. @@ -263,34 +265,62 @@ class MgmtActionResourceTest extends AbstractManagementApiIntegrationTest { @Test @Description("Verifies that all available actions are returned if the complete collection is requested.") void getActions() throws Exception { + getActions(false); + } + + @Test + @Description("Verifies that all available actions (whit ext refs) are returned if the complete collection is requested.") + void getActionsExtRef() throws Exception { + getActions(true); + } + + private void getActions(final boolean withExternalRef ) throws Exception { final String knownTargetId = "targetId"; final List actions = generateTargetWithTwoUpdatesWithOneOverride(knownTargetId); final Action action0 = actions.get(0); final Action action1 = actions.get(1); - mvc.perform(get(MgmtRestConstants.ACTION_V1_REQUEST_MAPPING).param(MgmtRestConstants.REQUEST_PARAMETER_SORTING, - "ID:ASC")).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) - // verify action 1 - .andExpect(jsonPath("content.[1].id", equalTo(action1.getId().intValue()))) - .andExpect(jsonPath("content.[1].type", equalTo("update"))) - .andExpect(jsonPath("content.[1].status", equalTo("pending"))) - .andExpect(jsonPath("content.[1].detailStatus", equalTo("running"))) - .andExpect(jsonPath("content.[1]._links.self.href", - equalTo(generateActionLink(knownTargetId, action1.getId())))) + final List externalRefs = new ArrayList<>(2); + if (withExternalRef) { + externalRefs.add("extRef#123_0"); + externalRefs.add("extRef#123_1"); + controllerManagement.updateActionExternalRef(action0.getId(), externalRefs.get(0)); + controllerManagement.updateActionExternalRef(action1.getId(), externalRefs.get(1)); + } - // verify action 0 - .andExpect(jsonPath("content.[0].id", equalTo(action0.getId().intValue()))) - .andExpect(jsonPath("content.[0].type", equalTo("cancel"))) - .andExpect(jsonPath("content.[0].status", equalTo("pending"))) - .andExpect(jsonPath("content.[1].detailStatus", equalTo("running"))) - .andExpect(jsonPath("content.[0]._links.self.href", - equalTo(generateActionLink(knownTargetId, action0.getId())))) + final ResultActions resultActions = + mvc.perform( + get(MgmtRestConstants.ACTION_V1_REQUEST_MAPPING) + .param(MgmtRestConstants.REQUEST_PARAMETER_SORTING,"ID:ASC")) + .andDo(MockMvcResultPrinter.print()) + .andExpect(status().isOk()) + // verify action 1 + .andExpect(jsonPath("content.[1].id", equalTo(action1.getId().intValue()))) + .andExpect(jsonPath("content.[1].type", equalTo("update"))) + .andExpect(jsonPath("content.[1].status", equalTo("pending"))) + .andExpect(jsonPath("content.[1].detailStatus", equalTo("running"))) + .andExpect(jsonPath("content.[1]._links.self.href", + equalTo(generateActionLink(knownTargetId, action1.getId())))) - // verify collection properties - .andExpect(jsonPath(JSON_PATH_PAGED_LIST_TOTAL, equalTo(2))) - .andExpect(jsonPath(JSON_PATH_PAGED_LIST_SIZE, equalTo(2))) - .andExpect(jsonPath(JSON_PATH_PAGED_LIST_CONTENT, hasSize(2))); + // verify action 0 + .andExpect(jsonPath("content.[0].id", equalTo(action0.getId().intValue()))) + .andExpect(jsonPath("content.[0].type", equalTo("cancel"))) + .andExpect(jsonPath("content.[0].status", equalTo("pending"))) + .andExpect(jsonPath("content.[1].detailStatus", equalTo("running"))) + .andExpect(jsonPath("content.[0]._links.self.href", + equalTo(generateActionLink(knownTargetId, action0.getId())))) + + // verify collection properties + .andExpect(jsonPath(JSON_PATH_PAGED_LIST_TOTAL, equalTo(2))) + .andExpect(jsonPath(JSON_PATH_PAGED_LIST_SIZE, equalTo(2))) + .andExpect(jsonPath(JSON_PATH_PAGED_LIST_CONTENT, hasSize(2))); + + if (withExternalRef) { + resultActions + .andExpect(jsonPath("content.[1].externalRef", equalTo(externalRefs.get(1)))) + .andExpect(jsonPath("content.[0].externalRef", equalTo(externalRefs.get(0)))); + } } @Test @@ -345,6 +375,16 @@ class MgmtActionResourceTest extends AbstractManagementApiIntegrationTest { @Test @Description("Handles the GET request of retrieving a specific action.") public void getAction() throws Exception { + getAction(false); + } + + @Test + @Description("Handles the GET request of retrieving a specific action with external reference.") + public void getActionExtRef() throws Exception { + getAction(true); + } + + private void getAction(final boolean withExternalRef) throws Exception { final String knownTargetId = "targetId"; // prepare ds final DistributionSet ds = testdataFactory.createDistributionSet(); @@ -358,10 +398,19 @@ class MgmtActionResourceTest extends AbstractManagementApiIntegrationTest { final List actions = deploymentManagement.findActionsByTarget(target.getControllerId(), PAGE) .getContent(); assertThat(actions).hasSize(1); + final String externalRef = "externalRef#123"; + if (withExternalRef) { + controllerManagement.updateActionExternalRef(actions.get(0).getId(), externalRef); + } - mvc.perform(get(MgmtRestConstants.ACTION_V1_REQUEST_MAPPING + "/{actionId}", actions.get(0).getId())) - .andDo(MockMvcResultPrinter.print()) - .andExpect(status().isOk()); + final ResultActions resultActions = + mvc.perform(get(MgmtRestConstants.ACTION_V1_REQUEST_MAPPING + "/{actionId}", actions.get(0).getId())) + .andDo(MockMvcResultPrinter.print()) + .andExpect(status().isOk()); + + if (withExternalRef) { + resultActions.andExpect(jsonPath("externalRef", equalTo(externalRef))); + } } @Test diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResourceTest.java b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResourceTest.java index 642bf90cd..e47300ceb 100644 --- a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResourceTest.java +++ b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResourceTest.java @@ -28,6 +28,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import java.time.Duration; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; @@ -100,6 +101,7 @@ import io.qameta.allure.Description; import io.qameta.allure.Feature; import io.qameta.allure.Step; import io.qameta.allure.Story; +import org.springframework.test.web.servlet.ResultActions; /** * Spring MVC Tests against the MgmtTargetResource. @@ -1088,25 +1090,50 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest { @Test @Description("Ensures that the expected response of getting actions of a target is returned.") void getActions() throws Exception { + getActions(false); + } + + @Test + @Description("Ensures that the expected response of getting actions (with ext refs) of a target is returned.") + void getActionsExtRef() throws Exception { + getActions(true); + } + + private void getActions(final boolean withExternalRef) throws Exception { final String knownTargetId = "targetId"; final List actions = generateTargetWithTwoUpdatesWithOneOverride(knownTargetId); - mvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId + "/" - + MgmtRestConstants.TARGET_V1_ACTIONS).param(MgmtRestConstants.REQUEST_PARAMETER_SORTING, "ID:ASC")) - .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) - .andExpect(jsonPath("content.[1].id", equalTo(actions.get(1).getId().intValue()))) - .andExpect(jsonPath("content.[1].type", equalTo("update"))) - .andExpect(jsonPath("content.[1].status", equalTo("pending"))) - .andExpect(jsonPath("content.[1]._links.self.href", - equalTo(generateActionSelfLink(knownTargetId, actions.get(1).getId())))) - .andExpect(jsonPath("content.[0].id", equalTo(actions.get(0).getId().intValue()))) - .andExpect(jsonPath("content.[0].type", equalTo("cancel"))) - .andExpect(jsonPath("content.[0].status", equalTo("pending"))) - .andExpect(jsonPath("content.[0]._links.self.href", - equalTo(generateActionSelfLink(knownTargetId, actions.get(0).getId())))) - .andExpect(jsonPath(JSON_PATH_PAGED_LIST_TOTAL, equalTo(2))) - .andExpect(jsonPath(JSON_PATH_PAGED_LIST_SIZE, equalTo(2))) - .andExpect(jsonPath(JSON_PATH_PAGED_LIST_CONTENT, hasSize(2))); + final List externalRefs = new ArrayList<>(2); + if (withExternalRef) { + externalRefs.add("extRef#123_0"); + externalRefs.add("extRef#123_1"); + controllerManagement.updateActionExternalRef(actions.get(0).getId(), externalRefs.get(0)); + controllerManagement.updateActionExternalRef(actions.get(1).getId(), externalRefs.get(1)); + } + + final ResultActions resultActions = + mvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId + "/" + + MgmtRestConstants.TARGET_V1_ACTIONS).param(MgmtRestConstants.REQUEST_PARAMETER_SORTING, "ID:ASC")) + .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) + .andExpect(jsonPath("content.[1].id", equalTo(actions.get(1).getId().intValue()))) + .andExpect(jsonPath("content.[1].type", equalTo("update"))) + .andExpect(jsonPath("content.[1].status", equalTo("pending"))) + .andExpect(jsonPath("content.[1]._links.self.href", + equalTo(generateActionSelfLink(knownTargetId, actions.get(1).getId())))) + .andExpect(jsonPath("content.[0].id", equalTo(actions.get(0).getId().intValue()))) + .andExpect(jsonPath("content.[0].type", equalTo("cancel"))) + .andExpect(jsonPath("content.[0].status", equalTo("pending"))) + .andExpect(jsonPath("content.[0]._links.self.href", + equalTo(generateActionSelfLink(knownTargetId, actions.get(0).getId())))) + .andExpect(jsonPath(JSON_PATH_PAGED_LIST_TOTAL, equalTo(2))) + .andExpect(jsonPath(JSON_PATH_PAGED_LIST_SIZE, equalTo(2))) + .andExpect(jsonPath(JSON_PATH_PAGED_LIST_CONTENT, hasSize(2))); + + if (withExternalRef) { + resultActions + .andExpect(jsonPath("content.[1].externalRef", equalTo(externalRefs.get(1)))) + .andExpect(jsonPath("content.[0].externalRef", equalTo(externalRefs.get(0)))); + } } @Test