Feature Approval Workflow for rollouts (#678)

* implement feature approval workflow

Signed-off-by: Ioannis Spyropoulos <ioannis.spyropoulos@bosch-si.com>
Signed-off-by: Michael Müller <Michael.Mueller17@bosch-si.com>

* add documentation for REST endpoints

Signed-off-by: Ioannis Spyropoulos <ioannis.spyropoulos@bosch-si.com>
Signed-off-by: Michael Müller <Michael.Mueller17@bosch-si.com>

* fix broken documentation test

Signed-off-by: Ioannis Spyropoulos <ioannis.spyropoulos@bosch-si.com>

* fix broken documentation test II

Signed-off-by: Ioannis Spyropoulos <ioannis.spyropoulos@bosch-si.com>
This commit is contained in:
Michael Müller
2018-06-04 16:36:56 +02:00
committed by Dominic Schabel
parent 8a14c931c8
commit cef7c2bbf2
43 changed files with 1056 additions and 43 deletions

View File

@@ -81,6 +81,36 @@ public interface MgmtRolloutRestApi {
MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<MgmtRolloutResponseBody> create(MgmtRolloutRestRequestBody rolloutRequestBody);
/**
* Handles the request for approving a rollout.
*
* @param rolloutId
* the ID of the rollout to be approved.
* @param remark
* an optional remark on the approval decision
* @return OK response (200) if rollout is approved now. In case of any
* exception the corresponding errors occur.
*/
@RequestMapping(method = RequestMethod.POST, value = "/{rolloutId}/approve", produces = { MediaTypes.HAL_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<Void> approve(@PathVariable("rolloutId") Long rolloutId,
@RequestParam(value = "remark", required = false) String remark);
/**
* Handles the request for denying the approval of a rollout.
*
* @param rolloutId
* the ID of the rollout to be denied.
* @param remark
* an optional remark on the denial decision
* @return OK response (200) if rollout is denied now. In case of any
* exception the corresponding errors occur.
*/
@RequestMapping(method = RequestMethod.POST, value = "/{rolloutId}/deny", produces = { MediaTypes.HAL_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<Void> deny(@PathVariable("rolloutId") Long rolloutId,
@RequestParam(value = "remark", required = false) String remark);
/**
* Handles the POST request for starting a rollout.
*

View File

@@ -88,6 +88,8 @@ final class MgmtRolloutMapper {
body.add(linkTo(methodOn(MgmtRolloutRestApi.class).start(rollout.getId())).withRel("start"));
body.add(linkTo(methodOn(MgmtRolloutRestApi.class).pause(rollout.getId())).withRel("pause"));
body.add(linkTo(methodOn(MgmtRolloutRestApi.class).resume(rollout.getId())).withRel("resume"));
body.add(linkTo(methodOn(MgmtRolloutRestApi.class).approve(rollout.getId(), null)).withRel("approve"));
body.add(linkTo(methodOn(MgmtRolloutRestApi.class).deny(rollout.getId(), null)).withRel("deny"));
body.add(linkTo(methodOn(MgmtRolloutRestApi.class).getRolloutGroups(rollout.getId(),
MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET_VALUE,
MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT_VALUE, null, null)).withRel("groups"));

View File

@@ -129,6 +129,18 @@ public class MgmtRolloutResource implements MgmtRolloutRestApi {
return ResponseEntity.status(HttpStatus.CREATED).body(MgmtRolloutMapper.toResponseRollout(rollout, true));
}
@Override
public ResponseEntity<Void> approve(@PathVariable("rolloutId") final Long rolloutId, final String remark) {
rolloutManagement.approveOrDeny(rolloutId, Rollout.ApprovalDecision.APPROVED, remark);
return ResponseEntity.ok().build();
}
@Override
public ResponseEntity<Void> deny(@PathVariable("rolloutId") final Long rolloutId, final String remark) {
rolloutManagement.approveOrDeny(rolloutId, Rollout.ApprovalDecision.DENIED, remark);
return ResponseEntity.ok().build();
}
@Override
public ResponseEntity<Void> start(@PathVariable("rolloutId") final Long rolloutId) {
this.rolloutManagement.start(rolloutId);

View File

@@ -179,6 +179,81 @@ include::../errors/415.adoc[]
include::../errors/429.adoc[]
|===
== POST /rest/v1/rollouts/{rolloutId}/approve
=== Implementation Notes
Handles the POST request of approving a created rollout within SP.
Only possible if approval workflow is enabled in system configuration and rollout is in state WAITING_FOR_APPROVAL.
Required Permission: APPROVE_ROLLOUT
=== Approve Rollout
==== CURL
include::{snippets}/rollouts/approve-rollout/curl-request.adoc[]
==== Request URL
include::{snippets}/rollouts/approve-rollout/http-request.adoc[]
=== Response (Status 200)
==== Response example
include::{snippets}/rollouts/approve-rollout/http-response.adoc[]
=== Error responses
|===
| HTTP Status Code | Reason | Response Model
include::../errors/400.adoc[]
include::../errors/401.adoc[]
include::../errors/403.adoc[]
include::../errors/405.adoc[]
include::../errors/406.adoc[]
include::../errors/429.adoc[]
|===
== POST /rest/v1/rollouts/{rolloutId}/deny
=== Implementation Notes
Handles the POST request of denying a created rollout within SP.
Only possible if approval workflow is enabled in system configuration and rollout is in state WAITING_FOR_APPROVAL.
Required Permission: APPROVE_ROLLOUT
=== Deny Rollout
==== CURL
include::{snippets}/rollouts/deny-rollout/curl-request.adoc[]
==== Request URL
include::{snippets}/rollouts/deny-rollout/http-request.adoc[]
=== Response (Status 200)
==== Response example
include::{snippets}/rollouts/deny-rollout/http-response.adoc[]
=== Error responses
|===
| HTTP Status Code | Reason | Response Model
include::../errors/400.adoc[]
include::../errors/401.adoc[]
include::../errors/403.adoc[]
include::../errors/405.adoc[]
include::../errors/406.adoc[]
include::../errors/429.adoc[]
|===
== POST /rest/v1/rollouts/{rolloutId}/start
=== Implementation Notes

View File

@@ -103,6 +103,8 @@ public final class MgmtApiModelProperties {
public static final String ROLLOUT_LINKS_START_ASYNC = "Link to start the rollout in async mode";
public static final String ROLLOUT_LINKS_PAUSE = "Link to pause a running rollout";
public static final String ROLLOUT_LINKS_RESUME = "Link to resume a paused rollout";
public static final String ROLLOUT_LINKS_APPROVE = "Link to approve a rollout";
public static final String ROLLOUT_LINKS_DENY = "Link to deny a rollout";
public static final String ROLLOUT_LINKS_GROUPS = "Link to retrieve the groups a rollout";
public static final String ROLLOUT_START_ASYNC = "Start the rollout asynchronous";

View File

@@ -33,6 +33,7 @@ import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupSuccessActi
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupSuccessCondition;
import org.eclipse.hawkbit.repository.model.RolloutGroupConditionBuilder;
import org.eclipse.hawkbit.repository.model.RolloutGroupConditions;
import org.eclipse.hawkbit.repository.test.util.RolloutTestApprovalStrategy;
import org.eclipse.hawkbit.rest.documentation.AbstractApiRestDocumentation;
import org.eclipse.hawkbit.rest.documentation.ApiModelPropertiesGeneric;
import org.eclipse.hawkbit.rest.documentation.DocumenationResponseFieldsSnippet;
@@ -41,6 +42,7 @@ import org.eclipse.hawkbit.rest.util.JsonBuilder;
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.hateoas.MediaTypes;
import org.springframework.http.MediaType;
@@ -62,12 +64,16 @@ import ru.yandex.qatools.allure.annotations.Stories;
@Stories("Rollout Resource")
public class RolloutResourceDocumentationTest extends AbstractApiRestDocumentation {
@Autowired
private RolloutTestApprovalStrategy approvalStrategy;
@Override
@Before
public void setUp() {
this.resourceName = "rollouts";
super.setUp();
arrayPrefix = "content[].";
approvalStrategy.setApprovalNeeded(false);
}
@Test
@@ -127,7 +133,6 @@ public class RolloutResourceDocumentationTest extends AbstractApiRestDocumentati
allFieldDescriptor.add(
fieldWithPath(arrayPrefix + "totalTargets").description(MgmtApiModelProperties.ROLLOUT_TOTAL_TARGETS));
allFieldDescriptor.add(fieldWithPath(arrayPrefix + "_links.self").ignored());
if (withDetails) {
allFieldDescriptor.add(fieldWithPath(arrayPrefix + "totalTargetsPerStatus")
.description(MgmtApiModelProperties.ROLLOUT_TOTAL_TARGETS_PER_STATUS));
@@ -139,6 +144,10 @@ public class RolloutResourceDocumentationTest extends AbstractApiRestDocumentati
.description(MgmtApiModelProperties.ROLLOUT_LINKS_RESUME));
allFieldDescriptor.add(fieldWithPath(arrayPrefix + "_links.groups")
.description(MgmtApiModelProperties.ROLLOUT_LINKS_GROUPS));
allFieldDescriptor.add(fieldWithPath(arrayPrefix + "_links.approve")
.description(MgmtApiModelProperties.ROLLOUT_LINKS_APPROVE));
allFieldDescriptor.add(fieldWithPath(arrayPrefix + "_links.deny")
.description(MgmtApiModelProperties.ROLLOUT_LINKS_DENY));
}
return new DocumenationResponseFieldsSnippet(allFieldDescriptor);
@@ -386,6 +395,29 @@ public class RolloutResourceDocumentationTest extends AbstractApiRestDocumentati
pathParameters(parameterWithName("rolloutId").description(ApiModelPropertiesGeneric.ITEM_ID))));
}
@Test
@Description("Handles the POST request of approving a rollout. Required Permission: "
+ SpPermission.APPROVE_ROLLOUT)
public void approveRollout() throws Exception {
approvalStrategy.setApprovalNeeded(true);
final Rollout rollout = createRolloutEntity();
mockMvc.perform(post(MgmtRestConstants.ROLLOUT_V1_REQUEST_MAPPING + "/{rolloutId}/approve", rollout.getId())
.accept(MediaTypes.HAL_JSON_VALUE)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andDo(this.document.document(
pathParameters(parameterWithName("rolloutId").description(ApiModelPropertiesGeneric.ITEM_ID))));
}
@Test
@Description("Handles the POST request of denying a rollout. Required Permission: " + SpPermission.APPROVE_ROLLOUT)
public void denyRollout() throws Exception {
approvalStrategy.setApprovalNeeded(true);
final Rollout rollout = createRolloutEntity();
mockMvc.perform(post(MgmtRestConstants.ROLLOUT_V1_REQUEST_MAPPING + "/{rolloutId}/deny", rollout.getId())
.accept(MediaTypes.HAL_JSON_VALUE)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andDo(this.document.document(
pathParameters(parameterWithName("rolloutId").description(ApiModelPropertiesGeneric.ITEM_ID))));
}
@Test
@Description("Handles the GET request of retrieving the deploy groups of a rollout. Required Permission: "
+ SpPermission.READ_ROLLOUT)

View File

@@ -80,6 +80,8 @@ public class TenantResourceDocumentationTest extends AbstractApiRestDocumentatio
"if the anonymous download mode is enabled.");
CONFIG_ITEM_DESCRIPTIONS.put(TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED,
"if autoclose running actions with new Distribution Set assignment is enabled.");
CONFIG_ITEM_DESCRIPTIONS.put(TenantConfigurationKey.ROLLOUT_APPROVAL_ENABLED,
"if approval mode for Rollout Management is enabled.");
}
@Autowired