Initial commit (#1345)

This commit is contained in:
Stefan Behl
2023-04-06 16:47:45 +02:00
committed by GitHub
parent fccc9871e6
commit a2fd46c732
6 changed files with 241 additions and 127 deletions

View File

@@ -102,6 +102,31 @@ public interface RolloutGroupManagement {
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
Page<RolloutGroup> findByRolloutAndRsql(@NotNull Pageable pageable, long rolloutId, Page<RolloutGroup> findByRolloutAndRsql(@NotNull Pageable pageable, long rolloutId,
@NotNull String rsqlParam); @NotNull String rsqlParam);
/**
* Retrieves a page of {@link RolloutGroup}s filtered by a given
* {@link Rollout} and a rsql filter with detailed status.
*
* @param pageable
* the page request to sort and limit the result
* @param rolloutId
* the rollout to filter the {@link RolloutGroup}s
* @param rsqlParam
* the specification to filter the result set based on attributes
* of the {@link RolloutGroup}
*
* @return a page of found {@link RolloutGroup}s
*
* @throws RSQLParameterUnsupportedFieldException
* if a field in the RSQL string is used but not provided by the
* given {@code fieldNameProvider}
* @throws RSQLParameterSyntaxException
* if the RSQL syntax is wrong
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
Page<RolloutGroup> findByRolloutAndRsqlWithDetailedStatus(@NotNull Pageable pageable, long rolloutId,
@NotNull String rsqlParam);
/** /**
* Retrieves a page of {@link RolloutGroup}s filtered by a given * Retrieves a page of {@link RolloutGroup}s filtered by a given

View File

@@ -157,6 +157,33 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
return JpaManagementHelper.convertPage(rolloutGroups, pageable); return JpaManagementHelper.convertPage(rolloutGroups, pageable);
} }
@Override
public Page<RolloutGroup> findByRolloutAndRsqlWithDetailedStatus(final Pageable pageable, final long rolloutId,
final String rsqlParam) {
throwEntityNotFoundExceptionIfRolloutDoesNotExist(rolloutId);
final Page<RolloutGroup> rolloutGroups = findByRolloutAndRsql(pageable, rolloutId, rsqlParam);
final List<Long> rolloutGroupIds = rolloutGroups.getContent().stream().map(RolloutGroup::getId)
.collect(Collectors.toList());
if (rolloutGroupIds.isEmpty()) {
// groups might already deleted, so return empty list.
return new PageImpl<>(Collections.emptyList());
}
final Map<Long, List<TotalTargetCountActionStatus>> allStatesForRollout = getStatusCountItemForRolloutGroup(
rolloutGroupIds);
for (final RolloutGroup rolloutGroup : rolloutGroups) {
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(
allStatesForRollout.get(rolloutGroup.getId()), Long.valueOf(rolloutGroup.getTotalTargets()),
rolloutGroup.getRollout().getActionType());
((JpaRolloutGroup) rolloutGroup).setTotalTargetCountStatus(totalTargetCountStatus);
}
return JpaManagementHelper.convertPage(rolloutGroups, pageable);
}
@Override @Override
public Optional<RolloutGroup> getWithDetailedStatus(final long rolloutGroupId) { public Optional<RolloutGroup> getWithDetailedStatus(final long rolloutGroupId) {
final Optional<RolloutGroup> rolloutGroup = get(rolloutGroupId); final Optional<RolloutGroup> rolloutGroup = get(rolloutGroupId);
@@ -265,15 +292,14 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
return new PageImpl<>(targetWithActionStatus, pageRequest, 0); return new PageImpl<>(targetWithActionStatus, pageRequest, 0);
} }
private Predicate getRolloutGroupTargetWithRolloutGroupJoinCondition(final long rolloutGroupId, final CriteriaBuilder cb, private Predicate getRolloutGroupTargetWithRolloutGroupJoinCondition(final long rolloutGroupId,
final Root<RolloutTargetGroup> targetRoot) { final CriteriaBuilder cb, final Root<RolloutTargetGroup> targetRoot) {
return cb.equal(targetRoot.get(RolloutTargetGroup_.rolloutGroup).get(JpaRolloutGroup_.id), // return cb.equal(targetRoot.get(RolloutTargetGroup_.rolloutGroup).get(JpaRolloutGroup_.id), //
rolloutGroupId); rolloutGroupId);
} }
private TargetWithActionStatus getTargetWithActionStatusFromQuery(final Object[] o) { private TargetWithActionStatus getTargetWithActionStatusFromQuery(final Object[] o) {
return new TargetWithActionStatus((Target) o[0], (Action.Status) o[1], return new TargetWithActionStatus((Target) o[0], (Action.Status) o[1], (Integer) o[2]);
(Integer) o[2]);
} }
private List<Order> getOrderBy(final Pageable pageRequest, final CriteriaBuilder cb, private List<Order> getOrderBy(final Pageable pageRequest, final CriteriaBuilder cb,
@@ -283,7 +309,8 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
return pageRequest.getSort().get().flatMap(order -> { return pageRequest.getSort().get().flatMap(order -> {
final List<Order> orders; final List<Order> orders;
final String property = order.getProperty(); final String property = order.getProperty();
// we consider status, last_action_status_code as property from JpaAction ... // we consider status, last_action_status_code as property from
// JpaAction ...
if ("status".equals(property) || "lastActionStatusCode".equals(property)) { if ("status".equals(property) || "lastActionStatusCode".equals(property)) {
orders = QueryUtils.toOrders(Sort.by(order.getDirection(), property), actionJoin, cb); orders = QueryUtils.toOrders(Sort.by(order.getDirection(), property), actionJoin, cb);
} }
@@ -302,7 +329,8 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
final CriteriaBuilder cb = entityManager.getCriteriaBuilder(); final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaQuery<Long> countQuery = cb.createQuery(Long.class); final CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
final Root<RolloutTargetGroup> countQueryFrom = countQuery.from(RolloutTargetGroup.class); final Root<RolloutTargetGroup> countQueryFrom = countQuery.from(RolloutTargetGroup.class);
countQuery.select(cb.count(countQueryFrom)).where(getRolloutGroupTargetWithRolloutGroupJoinCondition(rolloutGroupId, cb, countQueryFrom)); countQuery.select(cb.count(countQueryFrom))
.where(getRolloutGroupTargetWithRolloutGroupJoinCondition(rolloutGroupId, cb, countQueryFrom));
return entityManager.createQuery(countQuery).getSingleResult(); return entityManager.createQuery(countQuery).getSingleResult();
} }

View File

@@ -180,6 +180,10 @@ public interface MgmtRolloutRestApi {
* @param rsqlParam * @param rsqlParam
* the search parameter in the request URL, syntax * the search parameter in the request URL, syntax
* {@code q=name==abc} * {@code q=name==abc}
* @param representationModeParam
* the representation mode parameter specifying whether a compact
* or a full representation shall be returned
*
* @return a list of all rollout groups referred to a rollout for a defined * @return a list of all rollout groups referred to a rollout for a defined
* or default page request with status OK. The response is always * or default page request with status OK. The response is always
* paged. In any failure the JsonResponseExceptionHandler is * paged. In any failure the JsonResponseExceptionHandler is
@@ -191,7 +195,8 @@ public interface MgmtRolloutRestApi {
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) int pagingOffsetParam, @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) int pagingOffsetParam,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) int pagingLimitParam, @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) int pagingLimitParam,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) String sortParam, @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) String sortParam,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) String rsqlParam); @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 for retrieving a single rollout group. * Handles the GET request for retrieving a single rollout group.

View File

@@ -105,7 +105,7 @@ final class MgmtRolloutMapper {
body.add(linkTo(methodOn(MgmtRolloutRestApi.class).deny(rollout.getId(), null)).withRel("deny").expand()); body.add(linkTo(methodOn(MgmtRolloutRestApi.class).deny(rollout.getId(), null)).withRel("deny").expand());
body.add(linkTo(methodOn(MgmtRolloutRestApi.class).getRolloutGroups(rollout.getId(), body.add(linkTo(methodOn(MgmtRolloutRestApi.class).getRolloutGroups(rollout.getId(),
MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET_VALUE, MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET_VALUE,
MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT_VALUE, null, null)).withRel("groups") MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT_VALUE, null, null, null)).withRel("groups")
.expand()); .expand());
final DistributionSet distributionSet = rollout.getDistributionSet(); final DistributionSet distributionSet = rollout.getDistributionSet();
@@ -165,12 +165,12 @@ final class MgmtRolloutMapper {
} }
static List<MgmtRolloutGroupResponseBody> toResponseRolloutGroup(final List<RolloutGroup> rollouts, static List<MgmtRolloutGroupResponseBody> toResponseRolloutGroup(final List<RolloutGroup> rollouts,
final boolean confirmationFlowEnabled) { final boolean confirmationFlowEnabled, final boolean withDetails) {
if (rollouts == null) { if (rollouts == null) {
return Collections.emptyList(); return Collections.emptyList();
} }
return rollouts.stream().map(group -> toResponseRolloutGroup(group, false, confirmationFlowEnabled)) return rollouts.stream().map(group -> toResponseRolloutGroup(group, withDetails, confirmationFlowEnabled))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }

View File

@@ -99,11 +99,7 @@ public class MgmtRolloutResource implements MgmtRolloutRestApi {
final int sanitizedLimitParam = PagingUtility.sanitizePageLimitParam(pagingLimitParam); final int sanitizedLimitParam = PagingUtility.sanitizePageLimitParam(pagingLimitParam);
final Sort sorting = PagingUtility.sanitizeRolloutSortParam(sortParam); final Sort sorting = PagingUtility.sanitizeRolloutSortParam(sortParam);
final boolean isFullMode = MgmtRepresentationMode.fromValue(representationModeParam).orElseGet(() -> { final boolean isFullMode = parseRepresentationMode(representationModeParam) == MgmtRepresentationMode.FULL;
// no need for a 400, just apply a safe fallback
LOG.warn("Received an invalid representation mode: {}", representationModeParam);
return MgmtRepresentationMode.COMPACT;
}) == MgmtRepresentationMode.FULL;
final Pageable pageable = new OffsetBasedPageRequest(sanitizedOffsetParam, sanitizedLimitParam, sorting); final Pageable pageable = new OffsetBasedPageRequest(sanitizedOffsetParam, sanitizedLimitParam, sorting);
final Slice<Rollout> rollouts; final Slice<Rollout> rollouts;
@@ -237,23 +233,35 @@ public class MgmtRolloutResource implements MgmtRolloutRestApi {
@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,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) final int pagingLimitParam, @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) final int pagingLimitParam,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) final String sortParam, @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) final String sortParam,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) final String rsqlParam) { @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) final String rsqlParam,
final String representationModeParam) {
final int sanitizedOffsetParam = PagingUtility.sanitizeOffsetParam(pagingOffsetParam); final int sanitizedOffsetParam = PagingUtility.sanitizeOffsetParam(pagingOffsetParam);
final int sanitizedLimitParam = PagingUtility.sanitizePageLimitParam(pagingLimitParam); final int sanitizedLimitParam = PagingUtility.sanitizePageLimitParam(pagingLimitParam);
final Sort sorting = PagingUtility.sanitizeRolloutGroupSortParam(sortParam); final Sort sorting = PagingUtility.sanitizeRolloutGroupSortParam(sortParam);
final boolean isFullMode = parseRepresentationMode(representationModeParam) == MgmtRepresentationMode.FULL;
final Pageable pageable = new OffsetBasedPageRequest(sanitizedOffsetParam, sanitizedLimitParam, sorting); final Pageable pageable = new OffsetBasedPageRequest(sanitizedOffsetParam, sanitizedLimitParam, sorting);
final Page<RolloutGroup> findRolloutGroupsAll; final Page<RolloutGroup> rolloutGroups;
if (rsqlParam != null) { if (rsqlParam != null) {
findRolloutGroupsAll = this.rolloutGroupManagement.findByRolloutAndRsql(pageable, rolloutId, rsqlParam); if (isFullMode) {
rolloutGroups = this.rolloutGroupManagement.findByRolloutAndRsqlWithDetailedStatus(pageable,
rolloutId, rsqlParam);
} else {
rolloutGroups = this.rolloutGroupManagement.findByRolloutAndRsql(pageable, rolloutId, rsqlParam);
}
} else { } else {
findRolloutGroupsAll = this.rolloutGroupManagement.findByRollout(pageable, rolloutId); if (isFullMode) {
rolloutGroups = this.rolloutGroupManagement.findByRolloutWithDetailedStatus(pageable, rolloutId);
} else {
rolloutGroups = this.rolloutGroupManagement.findByRollout(pageable, rolloutId);
}
} }
final List<MgmtRolloutGroupResponseBody> rest = MgmtRolloutMapper.toResponseRolloutGroup( final List<MgmtRolloutGroupResponseBody> rest = MgmtRolloutMapper.toResponseRolloutGroup(
findRolloutGroupsAll.getContent(), tenantConfigHelper.isConfirmationFlowEnabled()); rolloutGroups.getContent(), tenantConfigHelper.isConfirmationFlowEnabled(), isFullMode);
return ResponseEntity.ok(new PagedList<>(rest, findRolloutGroupsAll.getTotalElements())); return ResponseEntity.ok(new PagedList<>(rest, rolloutGroups.getTotalElements()));
} }
@Override @Override
@@ -304,4 +312,13 @@ public class MgmtRolloutResource implements MgmtRolloutRestApi {
this.rolloutManagement.triggerNextGroup(rolloutId); this.rolloutManagement.triggerNextGroup(rolloutId);
return ResponseEntity.ok().build(); return ResponseEntity.ok().build();
} }
private static MgmtRepresentationMode parseRepresentationMode(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;
});
}
} }

View File

@@ -305,7 +305,7 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest {
rolloutManagement.start(rollout.getId()); rolloutManagement.start(rollout.getId());
rolloutHandler.handleAll(); rolloutHandler.handleAll();
// request the list of rollouts with full representation // request the list of rollouts with full representation
mvc.perform(get("/rest/v1/rollouts?representation=full").accept(MediaType.APPLICATION_JSON)) mvc.perform(get("/rest/v1/rollouts?representation=full").accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
@@ -592,10 +592,9 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest {
final Long forcetime = 45L; final Long forcetime = 45L;
testdataFactory.createTargets(20, "target", "rollout"); testdataFactory.createTargets(20, "target", "rollout");
postRollout("rollout1", 5, dsA.getId(), "id==target*", 20, Action.ActionType.TIMEFORCED, postRollout("rollout1", 5, dsA.getId(), "id==target*", 20, Action.ActionType.TIMEFORCED, startAt, forcetime);
startAt, forcetime); postRollout("rollout2", 5, dsA.getId(), "id==target-0001*", 10, Action.ActionType.TIMEFORCED, startAt,
postRollout("rollout2", 5, dsA.getId(), "id==target-0001*", 10, Action.ActionType.TIMEFORCED, forcetime);
startAt, forcetime);
// Run here, because Scheduler is disabled during tests // Run here, because Scheduler is disabled during tests
rolloutHandler.handleAll(); rolloutHandler.handleAll();
@@ -1165,7 +1164,8 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest {
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
.andExpect(jsonPath("$.content", hasSize(1))).andExpect(jsonPath("$.total", equalTo(1))) .andExpect(jsonPath("$.content", hasSize(1))).andExpect(jsonPath("$.total", equalTo(1)))
.andExpect(jsonPath("$.content[0].name", equalTo("group-1"))); .andExpect(jsonPath("$.content[0].name", equalTo("group-1")))
.andExpect(jsonPath("$.content[0].totalTargetsPerStatus").doesNotExist());
mvc.perform(get("/rest/v1/rollouts/{rolloutId}/deploygroups", rollout.getId()) mvc.perform(get("/rest/v1/rollouts/{rolloutId}/deploygroups", rollout.getId())
.accept(MediaType.APPLICATION_JSON).param(MgmtRestConstants.REQUEST_PARAMETER_SEARCH, "name==group*")) .accept(MediaType.APPLICATION_JSON).param(MgmtRestConstants.REQUEST_PARAMETER_SEARCH, "name==group*"))
@@ -1178,10 +1178,47 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest {
.param(MgmtRestConstants.REQUEST_PARAMETER_SEARCH, "name==group-1,name==group-2")) .param(MgmtRestConstants.REQUEST_PARAMETER_SEARCH, "name==group-1,name==group-2"))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
.andExpect(jsonPath("$.content", hasSize(2))).andExpect(jsonPath("$.total", equalTo(2))); .andExpect(jsonPath("$.content", hasSize(2))).andExpect(jsonPath("$.total", equalTo(2)))
.andExpect(jsonPath("$.content[0].totalTargetsPerStatus").doesNotExist())
.andExpect(jsonPath("$.content[1].totalTargetsPerStatus").doesNotExist());
} }
@Test
@Description("Testing that the list of rollout groups can be requested with representation mode 'full'.")
void retrieveRolloutGroupsFullRepresentation() throws Exception {
testdataFactory.createTargets(20, "rollout", "rollout");
final DistributionSet dsA = testdataFactory.createDistributionSet("");
// create a running rollout for the created targets
final Rollout rollout = rolloutManagement.create(
entityFactory.rollout().create().name("rollout1").set(dsA.getId())
.targetFilterQuery("controllerId==rollout*"),
4, false, new RolloutGroupConditionBuilder().withDefaults()
.successCondition(RolloutGroupSuccessCondition.THRESHOLD, "100").build());
rolloutHandler.handleAll();
rolloutManagement.start(rollout.getId());
rolloutHandler.handleAll();
// retrieve the rollout groups of the created rollout
// filter for the first group by RSQL
mvc.perform(get("/rest/v1/rollouts/{rolloutId}/deploygroups", rollout.getId())
.accept(MediaType.APPLICATION_JSON).param(MgmtRestConstants.REQUEST_PARAMETER_SEARCH, "name==group-1")
.param("representation", "full")).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
.andExpect(jsonPath("$.content", hasSize(1))).andExpect(jsonPath("$.total", equalTo(1)))
.andExpect(jsonPath("$.content[0].name", equalTo("group-1")))
.andExpect(jsonPath("content[0].totalTargetsPerStatus").exists())
.andExpect(jsonPath("content[0].totalTargetsPerStatus.running", equalTo(5)))
.andExpect(jsonPath("content[0].totalTargetsPerStatus.notstarted", equalTo(0)))
.andExpect(jsonPath("content[0].totalTargetsPerStatus.scheduled", equalTo(0)))
.andExpect(jsonPath("content[0].totalTargetsPerStatus.cancelled", equalTo(0)))
.andExpect(jsonPath("content[0].totalTargetsPerStatus.finished", equalTo(0)))
.andExpect(jsonPath("content[0].totalTargetsPerStatus.error", equalTo(0)));
}
@Test @Test
@Description("Verifies that a DOWNLOAD_ONLY rollout is possible") @Description("Verifies that a DOWNLOAD_ONLY rollout is possible")
void createDownloadOnlyRollout() throws Exception { void createDownloadOnlyRollout() throws Exception {
@@ -1199,7 +1236,8 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest {
final int weight = 66; final int weight = 66;
final String invalideWeightRequest = JsonBuilder.rollout("withWeight", "d", 2, dsId, "id==rollout*", final String invalideWeightRequest = JsonBuilder.rollout("withWeight", "d", 2, dsId, "id==rollout*",
new RolloutGroupConditionBuilder().withDefaults().build(), null, null, Action.WEIGHT_MIN - 1, null, null, null); new RolloutGroupConditionBuilder().withDefaults().build(), null, null, Action.WEIGHT_MIN - 1, null,
null, null);
final String valideWeightRequest = JsonBuilder.rollout("withWeight", "d", 2, dsId, "id==rollout*", final String valideWeightRequest = JsonBuilder.rollout("withWeight", "d", 2, dsId, "id==rollout*",
new RolloutGroupConditionBuilder().withDefaults().build(), null, null, weight, null, null, null); new RolloutGroupConditionBuilder().withDefaults().build(), null, null, weight, null, null, null);
@@ -1221,18 +1259,18 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest {
assertThat(rollouts.get(0).getWeight()).get().isEqualTo(weight); assertThat(rollouts.get(0).getWeight()).get().isEqualTo(weight);
} }
private void postRollout(final String name, final int groupSize, final Long distributionSetId, private void postRollout(final String name, final int groupSize, final Long distributionSetId,
final String targetFilterQuery, final int targets, final Action.ActionType type) throws Exception { final String targetFilterQuery, final int targets, final Action.ActionType type) throws Exception {
postRollout(name, groupSize, distributionSetId, targetFilterQuery, targets, type, null, null); postRollout(name, groupSize, distributionSetId, targetFilterQuery, targets, type, null, null);
} }
private void postRollout(final String name, final int groupSize, final Long distributionSetId, private void postRollout(final String name, final int groupSize, final Long distributionSetId,
final String targetFilterQuery, final int targets, final Action.ActionType type, final Long startTime, final Long forceTime) throws Exception { final String targetFilterQuery, final int targets, final Action.ActionType type, final Long startTime,
final Long forceTime) throws Exception {
final String actionType = MgmtRestModelMapper.convertActionType(type).getName(); final String actionType = MgmtRestModelMapper.convertActionType(type).getName();
final String rollout = JsonBuilder.rollout(name, "desc", groupSize, distributionSetId, targetFilterQuery, final String rollout = JsonBuilder.rollout(name, "desc", groupSize, distributionSetId, targetFilterQuery,
new RolloutGroupConditionBuilder().withDefaults().build(), null, actionType, null, startTime, new RolloutGroupConditionBuilder().withDefaults().build(), null, actionType, null, startTime, forceTime,
forceTime, null); null);
mvc.perform(post("/rest/v1/rollouts").content(rollout).contentType(MediaType.APPLICATION_JSON) mvc.perform(post("/rest/v1/rollouts").content(rollout).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isCreated()) .accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isCreated())
@@ -1246,12 +1284,10 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest {
.andExpect(jsonPath("$.lastModifiedBy", equalTo("bumlux"))) .andExpect(jsonPath("$.lastModifiedBy", equalTo("bumlux")))
.andExpect(jsonPath("$.lastModifiedAt", not(equalTo(0)))) .andExpect(jsonPath("$.lastModifiedAt", not(equalTo(0))))
.andExpect(jsonPath("$.totalTargets", equalTo(targets))) .andExpect(jsonPath("$.totalTargets", equalTo(targets)))
.andExpect(startTime != null ? .andExpect(startTime != null ? jsonPath("$.startAt", equalTo(startTime.intValue()))
jsonPath("$.startAt", equalTo(startTime.intValue())) : jsonPath("$.startAt").doesNotExist())
: jsonPath("$.startAt").doesNotExist()) .andExpect(forceTime != null ? jsonPath("$.forcetime", equalTo(forceTime.intValue()))
.andExpect(forceTime != null ? : jsonPath("$.forcetime", equalTo(0)))
jsonPath("$.forcetime", equalTo(forceTime.intValue()))
: jsonPath("$.forcetime", equalTo(0)))
.andExpect(jsonPath("$.totalTargetsPerStatus.running", equalTo(0))) .andExpect(jsonPath("$.totalTargetsPerStatus.running", equalTo(0)))
.andExpect(jsonPath("$.totalTargetsPerStatus.notstarted", equalTo(targets))) .andExpect(jsonPath("$.totalTargetsPerStatus.notstarted", equalTo(targets)))
.andExpect(jsonPath("$.totalTargetsPerStatus.scheduled", equalTo(0))) .andExpect(jsonPath("$.totalTargetsPerStatus.scheduled", equalTo(0)))
@@ -1366,94 +1402,97 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest {
} }
private void retrieveAndCompareRolloutsContent(final DistributionSet dsA, final String urlTemplate, private void retrieveAndCompareRolloutsContent(final DistributionSet dsA, final String urlTemplate,
final boolean isFullRepresentation, final boolean isStartTypeScheduled, final Long startAt, final Long forcetime) throws Exception { final boolean isFullRepresentation, final boolean isStartTypeScheduled, final Long startAt,
final Long forcetime) throws Exception {
mvc.perform(get(urlTemplate).accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()) mvc.perform(get(urlTemplate).accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk()).andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) .andExpect(status().isOk()).andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
.andExpect(jsonPath("$.content", hasSize(2))).andExpect(jsonPath("$.total", equalTo(2))) .andExpect(jsonPath("$.content", hasSize(2))).andExpect(jsonPath("$.total", equalTo(2)))
.andExpect(jsonPath("content[0].name", equalTo("rollout1"))) .andExpect(jsonPath("content[0].name", equalTo("rollout1")))
.andExpect(jsonPath("content[0].status", equalTo("ready"))) .andExpect(jsonPath("content[0].status", equalTo("ready")))
.andExpect(jsonPath("content[0].targetFilterQuery", equalTo("id==target*"))) .andExpect(jsonPath("content[0].targetFilterQuery", equalTo("id==target*")))
.andExpect(jsonPath("content[0].distributionSetId", equalTo(dsA.getId().intValue()))) .andExpect(jsonPath("content[0].distributionSetId", equalTo(dsA.getId().intValue())))
.andExpect(jsonPath("content[0].createdBy", equalTo("bumlux"))) .andExpect(jsonPath("content[0].createdBy", equalTo("bumlux")))
.andExpect(jsonPath("content[0].createdAt", not(equalTo(0)))) .andExpect(jsonPath("content[0].createdAt", not(equalTo(0))))
.andExpect(jsonPath("content[0].lastModifiedBy", equalTo("bumlux"))) .andExpect(jsonPath("content[0].lastModifiedBy", equalTo("bumlux")))
.andExpect(jsonPath("content[0].lastModifiedAt", not(equalTo(0)))) .andExpect(jsonPath("content[0].lastModifiedAt", not(equalTo(0))))
.andExpect(jsonPath("content[0].totalTargets", equalTo(20))) .andExpect(jsonPath("content[0].totalTargets", equalTo(20)))
.andExpect(jsonPath("content[0].forcetime", equalTo(isStartTypeScheduled ? forcetime.intValue() : 0))) .andExpect(jsonPath("content[0].forcetime", equalTo(isStartTypeScheduled ? forcetime.intValue() : 0)))
.andExpect(isFullRepresentation ? jsonPath("$.content[0].totalTargetsPerStatus").exists() .andExpect(isFullRepresentation ? jsonPath("$.content[0].totalTargetsPerStatus").exists()
: jsonPath("content[0].totalTargetsPerStatus").doesNotExist()) : jsonPath("content[0].totalTargetsPerStatus").doesNotExist())
.andExpect(isFullRepresentation ? jsonPath("$.content[0].totalGroups", equalTo(5)) .andExpect(isFullRepresentation ? jsonPath("$.content[0].totalGroups", equalTo(5))
: jsonPath("content[0].totalGroups").doesNotExist()) : jsonPath("content[0].totalGroups").doesNotExist())
.andExpect(isFullRepresentation && isStartTypeScheduled ? jsonPath("$.content[0].startAt", equalTo(startAt.intValue())) .andExpect(isFullRepresentation && isStartTypeScheduled
: jsonPath("$.content[0].startAt").doesNotExist()) ? jsonPath("$.content[0].startAt", equalTo(startAt.intValue()))
.andExpect(isFullRepresentation : jsonPath("$.content[0].startAt").doesNotExist())
? jsonPath("$.content[0]._links.start.href", startsWith(HREF_ROLLOUT_PREFIX)) .andExpect(isFullRepresentation
: jsonPath("content[0]._links.start.href").doesNotExist()) ? jsonPath("$.content[0]._links.start.href", startsWith(HREF_ROLLOUT_PREFIX))
.andExpect(isFullRepresentation : jsonPath("content[0]._links.start.href").doesNotExist())
? jsonPath("$.content[0]._links.pause.href", startsWith(HREF_ROLLOUT_PREFIX)) .andExpect(isFullRepresentation
: jsonPath("content[0]._links.pause.href").doesNotExist()) ? jsonPath("$.content[0]._links.pause.href", startsWith(HREF_ROLLOUT_PREFIX))
.andExpect(isFullRepresentation : jsonPath("content[0]._links.pause.href").doesNotExist())
? jsonPath("$.content[0]._links.resume.href", startsWith(HREF_ROLLOUT_PREFIX)) .andExpect(isFullRepresentation
: jsonPath("content[0]._links.resume.href").doesNotExist()) ? jsonPath("$.content[0]._links.resume.href", startsWith(HREF_ROLLOUT_PREFIX))
.andExpect(isFullRepresentation : jsonPath("content[0]._links.resume.href").doesNotExist())
? jsonPath("$.content[0]._links.triggerNextGroup.href", startsWith(HREF_ROLLOUT_PREFIX)) .andExpect(isFullRepresentation
: jsonPath("content[0]._links.triggerNextGroup.href").doesNotExist()) ? jsonPath("$.content[0]._links.triggerNextGroup.href", startsWith(HREF_ROLLOUT_PREFIX))
.andExpect(isFullRepresentation : jsonPath("content[0]._links.triggerNextGroup.href").doesNotExist())
? jsonPath("$.content[0]._links.approve.href", startsWith(HREF_ROLLOUT_PREFIX)) .andExpect(isFullRepresentation
: jsonPath("content[0]._links.approve.href").doesNotExist()) ? jsonPath("$.content[0]._links.approve.href", startsWith(HREF_ROLLOUT_PREFIX))
.andExpect(isFullRepresentation : jsonPath("content[0]._links.approve.href").doesNotExist())
? jsonPath("$.content[0]._links.deny.href", startsWith(HREF_ROLLOUT_PREFIX)) .andExpect(isFullRepresentation
: jsonPath("content[0]._links.deny.href").doesNotExist()) ? jsonPath("$.content[0]._links.deny.href", startsWith(HREF_ROLLOUT_PREFIX))
.andExpect(isFullRepresentation : jsonPath("content[0]._links.deny.href").doesNotExist())
? jsonPath("$.content[0]._links.groups.href", startsWith(HREF_ROLLOUT_PREFIX)) .andExpect(isFullRepresentation
: jsonPath("content[0]._links.groups.href").doesNotExist()) ? jsonPath("$.content[0]._links.groups.href", startsWith(HREF_ROLLOUT_PREFIX))
.andExpect(isFullRepresentation : jsonPath("content[0]._links.groups.href").doesNotExist())
? jsonPath("$.content[0]._links.distributionset.href", .andExpect(isFullRepresentation
startsWith("http://localhost/rest/v1/distributionsets/")) ? jsonPath("$.content[0]._links.distributionset.href",
: jsonPath("content[0]._links.distributionset.href").doesNotExist()) startsWith("http://localhost/rest/v1/distributionsets/"))
.andExpect(jsonPath("content[0]._links.self.href", startsWith(HREF_ROLLOUT_PREFIX))) : jsonPath("content[0]._links.distributionset.href").doesNotExist())
.andExpect(jsonPath("content[1].name", equalTo("rollout2"))) .andExpect(jsonPath("content[0]._links.self.href", startsWith(HREF_ROLLOUT_PREFIX)))
.andExpect(jsonPath("content[1].status", equalTo("ready"))) .andExpect(jsonPath("content[1].name", equalTo("rollout2")))
.andExpect(jsonPath("content[1].targetFilterQuery", equalTo("id==target-0001*"))) .andExpect(jsonPath("content[1].status", equalTo("ready")))
.andExpect(jsonPath("content[1].distributionSetId", equalTo(dsA.getId().intValue()))) .andExpect(jsonPath("content[1].targetFilterQuery", equalTo("id==target-0001*")))
.andExpect(jsonPath("content[1].createdBy", equalTo("bumlux"))) .andExpect(jsonPath("content[1].distributionSetId", equalTo(dsA.getId().intValue())))
.andExpect(jsonPath("content[1].createdAt", not(equalTo(0)))) .andExpect(jsonPath("content[1].createdBy", equalTo("bumlux")))
.andExpect(jsonPath("content[1].lastModifiedBy", equalTo("bumlux"))) .andExpect(jsonPath("content[1].createdAt", not(equalTo(0))))
.andExpect(jsonPath("content[1].lastModifiedAt", not(equalTo(0)))) .andExpect(jsonPath("content[1].lastModifiedBy", equalTo("bumlux")))
.andExpect(jsonPath("content[1].totalTargets", equalTo(10))) .andExpect(jsonPath("content[1].lastModifiedAt", not(equalTo(0))))
.andExpect(jsonPath("content[1].forcetime", equalTo(isStartTypeScheduled ? forcetime.intValue() : 0))) .andExpect(jsonPath("content[1].totalTargets", equalTo(10)))
.andExpect(isFullRepresentation ? jsonPath("$.content[1].totalTargetsPerStatus").exists() .andExpect(jsonPath("content[1].forcetime", equalTo(isStartTypeScheduled ? forcetime.intValue() : 0)))
: jsonPath("content[1].totalTargetsPerStatus").doesNotExist()) .andExpect(isFullRepresentation ? jsonPath("$.content[1].totalTargetsPerStatus").exists()
.andExpect(isFullRepresentation ? jsonPath("$.content[1].totalGroups", equalTo(5)) : jsonPath("content[1].totalTargetsPerStatus").doesNotExist())
: jsonPath("content[1].totalGroups").doesNotExist()) .andExpect(isFullRepresentation ? jsonPath("$.content[1].totalGroups", equalTo(5))
.andExpect(isFullRepresentation && isStartTypeScheduled ? jsonPath("content[1].startAt", equalTo(startAt.intValue())) : jsonPath("content[1].totalGroups").doesNotExist())
: jsonPath("content[1].startAt").doesNotExist()) .andExpect(isFullRepresentation && isStartTypeScheduled
.andExpect(isFullRepresentation ? jsonPath("content[1].startAt", equalTo(startAt.intValue()))
? jsonPath("$.content[1]._links.start.href", startsWith(HREF_ROLLOUT_PREFIX)) : jsonPath("content[1].startAt").doesNotExist())
: jsonPath("content[1]._links.start.href").doesNotExist()) .andExpect(isFullRepresentation
.andExpect(isFullRepresentation ? jsonPath("$.content[1]._links.start.href", startsWith(HREF_ROLLOUT_PREFIX))
? jsonPath("$.content[1]._links.pause.href", startsWith(HREF_ROLLOUT_PREFIX)) : jsonPath("content[1]._links.start.href").doesNotExist())
: jsonPath("content[1]._links.pause.href").doesNotExist()) .andExpect(isFullRepresentation
.andExpect(isFullRepresentation ? jsonPath("$.content[1]._links.pause.href", startsWith(HREF_ROLLOUT_PREFIX))
? jsonPath("$.content[1]._links.resume.href", startsWith(HREF_ROLLOUT_PREFIX)) : jsonPath("content[1]._links.pause.href").doesNotExist())
: jsonPath("content[1]._links.resume.href").doesNotExist()) .andExpect(isFullRepresentation
.andExpect(isFullRepresentation ? jsonPath("$.content[1]._links.resume.href", startsWith(HREF_ROLLOUT_PREFIX))
? jsonPath("$.content[1]._links.triggerNextGroup.href", startsWith(HREF_ROLLOUT_PREFIX)) : jsonPath("content[1]._links.resume.href").doesNotExist())
: jsonPath("content[1]._links.triggerNextGroup.href").doesNotExist()) .andExpect(isFullRepresentation
.andExpect(isFullRepresentation ? jsonPath("$.content[1]._links.triggerNextGroup.href", startsWith(HREF_ROLLOUT_PREFIX))
? jsonPath("$.content[1]._links.approve.href", startsWith(HREF_ROLLOUT_PREFIX)) : jsonPath("content[1]._links.triggerNextGroup.href").doesNotExist())
: jsonPath("content[1]._links.approve.href").doesNotExist()) .andExpect(isFullRepresentation
.andExpect(isFullRepresentation ? jsonPath("$.content[1]._links.approve.href", startsWith(HREF_ROLLOUT_PREFIX))
? jsonPath("$.content[1]._links.deny.href", startsWith(HREF_ROLLOUT_PREFIX)) : jsonPath("content[1]._links.approve.href").doesNotExist())
: jsonPath("content[1]._links.deny.href").doesNotExist()) .andExpect(isFullRepresentation
.andExpect(isFullRepresentation ? jsonPath("$.content[1]._links.deny.href", startsWith(HREF_ROLLOUT_PREFIX))
? jsonPath("$.content[1]._links.groups.href", startsWith(HREF_ROLLOUT_PREFIX)) : jsonPath("content[1]._links.deny.href").doesNotExist())
: jsonPath("content[1]._links.groups.href").doesNotExist()) .andExpect(isFullRepresentation
.andExpect(isFullRepresentation ? jsonPath("$.content[1]._links.groups.href", startsWith(HREF_ROLLOUT_PREFIX))
? jsonPath("$.content[1]._links.distributionset.href", : jsonPath("content[1]._links.groups.href").doesNotExist())
startsWith("http://localhost/rest/v1/distributionsets/")) .andExpect(isFullRepresentation
: jsonPath("content[1]._links.distributionset.href").doesNotExist()) ? jsonPath("$.content[1]._links.distributionset.href",
.andExpect(jsonPath("content[1]._links.self.href", startsWith(HREF_ROLLOUT_PREFIX))); startsWith("http://localhost/rest/v1/distributionsets/"))
: jsonPath("content[1]._links.distributionset.href").doesNotExist())
.andExpect(jsonPath("content[1]._links.self.href", startsWith(HREF_ROLLOUT_PREFIX)));
} }
} }