Refactor action repository (#2118)
Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -144,9 +144,7 @@ public class DdiRootController implements DdiRootControllerRestApi {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<DdiControllerBase> getControllerBase(
|
||||
final String tenant,
|
||||
final String controllerId) {
|
||||
public ResponseEntity<DdiControllerBase> getControllerBase(final String tenant, final String controllerId) {
|
||||
log.debug("getControllerBase({})", controllerId);
|
||||
|
||||
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotExist(controllerId, IpUtil
|
||||
|
||||
@@ -81,9 +81,8 @@ import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* {@link AmqpMessageDispatcherService} create all outgoing AMQP messages and
|
||||
* delegate the messages to a {@link AmqpMessageSenderService}.
|
||||
*
|
||||
* {@link AmqpMessageDispatcherService} create all outgoing AMQP messages and delegate the messages to a {@link AmqpMessageSenderService}.
|
||||
* <p/>
|
||||
* Additionally, the dispatcher listener/subscribe for some target events e.g. assignment.
|
||||
*/
|
||||
@Slf4j
|
||||
@@ -173,7 +172,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
|
||||
}
|
||||
|
||||
log.debug("MultiActionEvent received for {}", multiActionEvent.getControllerIds());
|
||||
sendMultiActionRequestMessages(multiActionEvent.getTenant(), multiActionEvent.getControllerIds());
|
||||
sendMultiActionRequestMessages(multiActionEvent.getControllerIds());
|
||||
}
|
||||
|
||||
protected void sendUpdateMessageToTarget(
|
||||
@@ -184,27 +183,6 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
|
||||
sendUpdateMessageToTargets(actionProp, Collections.singletonList(target), softwareModules);
|
||||
}
|
||||
|
||||
protected void sendMultiActionRequestToTarget(
|
||||
final String tenant, final Target target, final List<Action> actions,
|
||||
final Function<Action, Map<SoftwareModule, List<SoftwareModuleMetadata>>> getSoftwareModuleMetaData) {
|
||||
final URI targetAddress = target.getAddress();
|
||||
if (!IpUtil.isAmqpUri(targetAddress) || CollectionUtils.isEmpty(actions)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final DmfMultiActionRequest multiActionRequest = new DmfMultiActionRequest();
|
||||
actions.forEach(action -> {
|
||||
final DmfActionRequest actionRequest = createDmfActionRequest(target, action, getSoftwareModuleMetaData.apply(action));
|
||||
final int weight = deploymentManagement.getWeightConsideringDefault(action);
|
||||
multiActionRequest.addElement(getEventTypeForAction(action), actionRequest, weight);
|
||||
});
|
||||
|
||||
final Message message = getMessageConverter().toMessage(
|
||||
multiActionRequest,
|
||||
createConnectorMessagePropertiesEvent(tenant, target.getControllerId(), EventTopic.MULTI_ACTION));
|
||||
amqpSenderService.sendMessage(message, targetAddress);
|
||||
}
|
||||
|
||||
protected DmfDownloadAndUpdateRequest createDownloadAndUpdateRequest(
|
||||
final Target target, final Long actionId,
|
||||
final Map<SoftwareModule, List<SoftwareModuleMetadata>> softwareModules) {
|
||||
@@ -303,14 +281,6 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
|
||||
return dmfTarget;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Confirmation request.
|
||||
*
|
||||
* @param target the target
|
||||
* @param actionId the actionId
|
||||
* @param softwareModules the software modules
|
||||
* @return confirm request
|
||||
*/
|
||||
protected DmfConfirmRequest createConfirmRequest(
|
||||
final Target target, final Long actionId, final Map<SoftwareModule, List<SoftwareModuleMetadata>> softwareModules) {
|
||||
final DmfConfirmRequest request = new DmfConfirmRequest();
|
||||
@@ -325,6 +295,33 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
|
||||
return request;
|
||||
}
|
||||
|
||||
void sendMultiActionRequestToTarget(
|
||||
final Target target, final List<Action> actions,
|
||||
final Function<SoftwareModule, List<SoftwareModuleMetadata>> getSoftwareModuleMetaData) {
|
||||
final URI targetAddress = target.getAddress();
|
||||
if (!IpUtil.isAmqpUri(targetAddress) || CollectionUtils.isEmpty(actions)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final DmfMultiActionRequest multiActionRequest = new DmfMultiActionRequest();
|
||||
actions.forEach(action -> {
|
||||
final DmfActionRequest actionRequest = createDmfActionRequest(
|
||||
target, action,
|
||||
action.getDistributionSet().getModules().stream()
|
||||
.collect(Collectors.toMap(Function.identity(), module -> {
|
||||
final List<SoftwareModuleMetadata> softwareModuleMetadata = getSoftwareModuleMetaData.apply(module);
|
||||
return softwareModuleMetadata == null ? Collections.emptyList() : softwareModuleMetadata;
|
||||
})));
|
||||
final int weight = deploymentManagement.getWeightConsideringDefault(action);
|
||||
multiActionRequest.addElement(getEventTypeForAction(action), actionRequest, weight);
|
||||
});
|
||||
|
||||
final Message message = getMessageConverter().toMessage(
|
||||
multiActionRequest,
|
||||
createConnectorMessagePropertiesEvent(target.getTenant(), target.getControllerId(), EventTopic.MULTI_ACTION));
|
||||
amqpSenderService.sendMessage(message, targetAddress);
|
||||
}
|
||||
|
||||
private static DmfActionRequest createPlainActionRequest(final Action action) {
|
||||
final DmfActionRequest actionRequest = new DmfActionRequest();
|
||||
actionRequest.setActionId(action.getId());
|
||||
@@ -463,23 +460,27 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
|
||||
}
|
||||
}
|
||||
|
||||
private void sendMultiActionRequestMessages(final String tenant, final List<String> controllerIds) {
|
||||
final Map<SoftwareModule, List<SoftwareModuleMetadata>> softwareModuleMetadata = new HashMap<>();
|
||||
targetManagement.getByControllerID(controllerIds).stream()
|
||||
.filter(target -> IpUtil.isAmqpUri(target.getAddress())).forEach(target -> {
|
||||
final List<Action> activeActions = deploymentManagement
|
||||
.findActiveActionsWithHighestWeight(target.getControllerId(), MAX_ACTION_COUNT);
|
||||
private void sendMultiActionRequestMessages(final List<String> controllerIds) {
|
||||
final Map<String, List<Action>> controllerIdToActions = controllerIds.stream()
|
||||
.collect(Collectors.toMap(
|
||||
Function.identity(),
|
||||
controllerId -> deploymentManagement.findActiveActionsWithHighestWeight(controllerId, MAX_ACTION_COUNT)));
|
||||
|
||||
activeActions.forEach(action ->
|
||||
action.getDistributionSet().getModules().forEach(module ->
|
||||
softwareModuleMetadata.computeIfAbsent(module, this::getSoftwareModuleMetadata)));
|
||||
// gets all software modules for all action at once
|
||||
final Set<Long> allSmIds = controllerIdToActions.values().stream()
|
||||
.flatMap(actions -> actions.stream()
|
||||
.map(Action::getDistributionSet)
|
||||
.flatMap(ds -> ds.getModules().stream())
|
||||
.map(SoftwareModule::getId))
|
||||
.collect(Collectors.toSet());
|
||||
final Map<Long, List<SoftwareModuleMetadata>> getSoftwareModuleMetadata =
|
||||
allSmIds.isEmpty()
|
||||
? Collections.emptyMap()
|
||||
: softwareModuleManagement.findMetaDataBySoftwareModuleIdsAndTargetVisible(allSmIds);
|
||||
|
||||
if (!activeActions.isEmpty()) {
|
||||
sendMultiActionRequestToTarget(tenant, target, activeActions,
|
||||
action -> action.getDistributionSet().getModules().stream()
|
||||
.collect(Collectors.toMap(m -> m, softwareModuleMetadata::get)));
|
||||
}
|
||||
});
|
||||
targetManagement.getByControllerID(controllerIds).forEach(target ->
|
||||
sendMultiActionRequestToTarget(
|
||||
target, controllerIdToActions.get(target.getControllerId()), module -> getSoftwareModuleMetadata.get(module.getId())));
|
||||
}
|
||||
|
||||
private DmfActionRequest createDmfActionRequest(
|
||||
@@ -602,7 +603,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
|
||||
}
|
||||
|
||||
private Map<SoftwareModule, List<SoftwareModuleMetadata>> getSoftwareModulesWithMetadata(final DistributionSet distributionSet) {
|
||||
return distributionSet.getModules().stream().collect(Collectors.toMap(m -> m, this::getSoftwareModuleMetadata));
|
||||
return distributionSet.getModules().stream().collect(Collectors.toMap(Function.identity(), this::getSoftwareModuleMetadata));
|
||||
}
|
||||
|
||||
private List<SoftwareModuleMetadata> getSoftwareModuleMetadata(final SoftwareModule module) {
|
||||
|
||||
@@ -20,6 +20,7 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -67,9 +68,8 @@ import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link AmqpMessageHandlerService} handles all incoming target interaction
|
||||
* AMQP messages (e.g. create target, check for updates etc.) for the queue
|
||||
* which is configured for the property hawkbit.dmf.rabbitmq.receiverQueue.
|
||||
* {@link AmqpMessageHandlerService} handles all incoming target interaction AMQP messages (e.g. create target, check for updates etc.) for the
|
||||
* queue which is configured for the property hawkbit.dmf.rabbitmq.receiverQueue.
|
||||
*/
|
||||
@Slf4j
|
||||
public class AmqpMessageHandlerService extends BaseAmqpService {
|
||||
@@ -344,41 +344,39 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
||||
private void sendCurrentActionsAsMultiActionToTarget(final Target target) {
|
||||
final List<Action> actions = controllerManagement.findActiveActionsWithHighestWeight(target.getControllerId(), MAX_ACTION_COUNT);
|
||||
|
||||
final Set<DistributionSet> distributionSets = actions.stream().map(Action::getDistributionSet).collect(Collectors.toSet());
|
||||
final Map<Long, Map<SoftwareModule, List<SoftwareModuleMetadata>>> softwareModulesPerDistributionSet = distributionSets
|
||||
.stream().collect(Collectors.toMap(DistributionSet::getId, this::getSoftwareModulesWithMetadata));
|
||||
// gets all software modules for all action at once
|
||||
final Set<Long> allSmIds = actions.stream()
|
||||
.map(Action::getDistributionSet)
|
||||
.flatMap(ds -> ds.getModules().stream())
|
||||
.map(SoftwareModule::getId)
|
||||
.collect(Collectors.toSet());
|
||||
final Map<Long, List<SoftwareModuleMetadata>> getSoftwareModuleMetadata =
|
||||
allSmIds.isEmpty() ? Collections.emptyMap() : controllerManagement.findTargetVisibleMetaDataBySoftwareModuleId(allSmIds);
|
||||
|
||||
amqpMessageDispatcherService.sendMultiActionRequestToTarget(
|
||||
target.getTenant(), target, actions,
|
||||
action -> softwareModulesPerDistributionSet.get(action.getDistributionSet().getId()));
|
||||
amqpMessageDispatcherService.sendMultiActionRequestToTarget(target, actions, module -> getSoftwareModuleMetadata.get(module.getId()));
|
||||
}
|
||||
|
||||
private void sendOldestActionToTarget(final Target target) {
|
||||
final Optional<Action> actionOptional = controllerManagement.findActiveActionWithHighestWeight(target.getControllerId());
|
||||
|
||||
if (actionOptional.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Action action = actionOptional.get();
|
||||
if (action.isCancelingOrCanceled()) {
|
||||
amqpMessageDispatcherService.sendCancelMessageToTarget(target.getTenant(), target.getControllerId(),
|
||||
action.getId(), target.getAddress());
|
||||
amqpMessageDispatcherService.sendCancelMessageToTarget(
|
||||
target.getTenant(), target.getControllerId(), action.getId(), target.getAddress());
|
||||
} else {
|
||||
amqpMessageDispatcherService.sendUpdateMessageToTarget(new ActionProperties(action), action.getTarget(),
|
||||
getSoftwareModulesWithMetadata(action.getDistributionSet()));
|
||||
amqpMessageDispatcherService.sendUpdateMessageToTarget(
|
||||
new ActionProperties(action), action.getTarget(), getSoftwareModulesWithMetadata(action.getDistributionSet()));
|
||||
}
|
||||
}
|
||||
|
||||
private Map<SoftwareModule, List<SoftwareModuleMetadata>> getSoftwareModulesWithMetadata(final DistributionSet distributionSet) {
|
||||
final List<Long> smIds = distributionSet.getModules().stream().map(SoftwareModule::getId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
final Map<Long, List<SoftwareModuleMetadata>> metadata = controllerManagement
|
||||
.findTargetVisibleMetaDataBySoftwareModuleId(smIds);
|
||||
|
||||
return distributionSet.getModules().stream()
|
||||
.collect(Collectors.toMap(sm -> sm, sm -> metadata.getOrDefault(sm.getId(), Collections.emptyList())));
|
||||
final List<Long> smIds = distributionSet.getModules().stream().map(SoftwareModule::getId).collect(Collectors.toList());
|
||||
final Map<Long, List<SoftwareModuleMetadata>> metadata = controllerManagement.findTargetVisibleMetaDataBySoftwareModuleId(smIds);
|
||||
return distributionSet.getModules().stream().collect(Collectors.toMap(
|
||||
Function.identity(), sm -> metadata.getOrDefault(sm.getId(), Collections.emptyList())));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -465,8 +465,9 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer
|
||||
assertLatestMultiActionMessageContainsInstallMessages(controllerId, Arrays.asList(smIds1, smIds2, smIds1));
|
||||
|
||||
final List<Long> installActions = getLatestMultiActionMessageActions(controllerId).stream()
|
||||
.filter(entry -> entry.getValue().equals(EventTopic.DOWNLOAD_AND_INSTALL)).map(Entry::getKey)
|
||||
.collect(Collectors.toList());
|
||||
.filter(entry -> entry.getValue().equals(EventTopic.DOWNLOAD_AND_INSTALL))
|
||||
.map(Entry::getKey)
|
||||
.toList();
|
||||
|
||||
updateActionViaDmfClient(controllerId, installActions.get(0), DmfActionStatus.FINISHED);
|
||||
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.REQUEST_ATTRIBUTES_UPDATE, EventTopic.MULTI_ACTION);
|
||||
|
||||
@@ -41,23 +41,19 @@ import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
|
||||
/**
|
||||
* Service layer for all operations of the DDI API (with access permissions only
|
||||
* for the controller).
|
||||
* Service layer for all operations of the DDI API (with access permissions only for the controller).
|
||||
*/
|
||||
public interface ControllerManagement {
|
||||
|
||||
/**
|
||||
* Adds an {@link ActionStatus} for a cancel {@link Action} including
|
||||
* potential state changes for the target and the {@link Action} itself.
|
||||
* Adds an {@link ActionStatus} for a cancel {@link Action} including potential state changes for the target and the {@link Action} itself.
|
||||
*
|
||||
* @param create to be added
|
||||
* @return the updated {@link Action}
|
||||
* @throws EntityAlreadyExistsException if a given entity already exists
|
||||
* @throws AssignmentQuotaExceededException if more than the allowed number of status entries or messages
|
||||
* per entry are inserted
|
||||
* @throws AssignmentQuotaExceededException if more than the allowed number of status entries or messages per entry are inserted
|
||||
* @throws EntityNotFoundException if given action does not exist
|
||||
* @throws ConstraintViolationException if fields are not filled as specified. Check
|
||||
* {@link ActionStatusCreate} for field constraints.
|
||||
* @throws ConstraintViolationException if fields are not filled as specified. Check {@link ActionStatusCreate} for field constraints.
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
|
||||
Action addCancelActionStatus(@NotNull @Valid ActionStatusCreate create);
|
||||
@@ -72,54 +68,45 @@ public interface ControllerManagement {
|
||||
Optional<SoftwareModule> getSoftwareModule(long moduleId);
|
||||
|
||||
/**
|
||||
* Retrieves {@link SoftwareModuleMetadata} where
|
||||
* {@link SoftwareModuleMetadata#isTargetVisible()}.
|
||||
* Retrieves {@link SoftwareModuleMetadata} where {@link SoftwareModuleMetadata#isTargetVisible()}.
|
||||
*
|
||||
* @param moduleId of the {@link SoftwareModule}
|
||||
* @return list of {@link SoftwareModuleMetadata} with maximum size of
|
||||
* @return the map of software module id to {@link SoftwareModuleMetadata} with maximum size of
|
||||
* {@link RepositoryConstants#MAX_META_DATA_COUNT}
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
|
||||
Map<Long, List<SoftwareModuleMetadata>> findTargetVisibleMetaDataBySoftwareModuleId(
|
||||
@NotNull Collection<Long> moduleId);
|
||||
Map<Long, List<SoftwareModuleMetadata>> findTargetVisibleMetaDataBySoftwareModuleId(@NotNull Collection<Long> moduleId);
|
||||
|
||||
/**
|
||||
* Simple addition of a new {@link ActionStatus} entry to the
|
||||
* {@link Action}. No state changes.
|
||||
* Simple addition of a new {@link ActionStatus} entry to the {@link Action}. No state changes.
|
||||
*
|
||||
* @param create to add to the action
|
||||
* @return created {@link ActionStatus} entity
|
||||
* @throws AssignmentQuotaExceededException if more than the allowed number of status entries or messages
|
||||
* per entry are inserted
|
||||
* @throws AssignmentQuotaExceededException if more than the allowed number of status entries or messages per entry are inserted
|
||||
* @throws EntityNotFoundException if given action does not exist
|
||||
* @throws ConstraintViolationException if fields are not filled as specified. Check
|
||||
* {@link ActionStatusCreate} for field constraints.
|
||||
* @throws ConstraintViolationException if fields are not filled as specified. Check {@link ActionStatusCreate} for field constraints.
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
|
||||
ActionStatus addInformationalActionStatus(@NotNull @Valid ActionStatusCreate create);
|
||||
|
||||
/**
|
||||
* Adds an {@link ActionStatus} entry for an update {@link Action} including
|
||||
* potential state changes for the target and the {@link Action} itself.
|
||||
* Adds an {@link ActionStatus} entry for an update {@link Action} including potential state changes for the target and the {@link Action}
|
||||
* itself.
|
||||
*
|
||||
* @param create to be added
|
||||
* @return the updated {@link Action}
|
||||
* @throws EntityAlreadyExistsException if a given entity already exists
|
||||
* @throws AssignmentQuotaExceededException if more than the allowed number of status entries or messages
|
||||
* per entry are inserted
|
||||
* @throws AssignmentQuotaExceededException if more than the allowed number of status entries or messages per entry are inserted
|
||||
* @throws EntityNotFoundException if action status not exist
|
||||
* @throws ConstraintViolationException if fields are not filled as specified. Check
|
||||
* {@link ActionStatusCreate} for field constraints.
|
||||
* @throws ConstraintViolationException if fields are not filled as specified. Check {@link ActionStatusCreate} for field constraints.
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
|
||||
Action addUpdateActionStatus(@NotNull @Valid ActionStatusCreate create);
|
||||
|
||||
/**
|
||||
* Retrieves active {@link Action} with highest priority that is assigned to
|
||||
* a {@link Target}.
|
||||
* Retrieves active {@link Action} with the highest priority that is assigned to a {@link Target}.
|
||||
*
|
||||
* For performance reasons this method does not throw
|
||||
* {@link EntityNotFoundException} in case target with given controllerId
|
||||
* For performance reasons this method does not throw {@link EntityNotFoundException} in case target with given controllerId
|
||||
* does not exist but will return an {@link Optional#empty()} instead.
|
||||
*
|
||||
* @param controllerId identifies the target to retrieve the action from
|
||||
@@ -140,17 +127,7 @@ public interface ControllerManagement {
|
||||
List<Action> findActiveActionsWithHighestWeight(@NotEmpty String controllerId, int maxActionCount);
|
||||
|
||||
/**
|
||||
* Get weight of an Action. Returns the default value if the weight is null
|
||||
* according to the properties.
|
||||
*
|
||||
* @param action to extract the weight from
|
||||
* @return weight of the action
|
||||
*/
|
||||
int getWeightConsideringDefault(final Action action);
|
||||
|
||||
/**
|
||||
* Get the {@link Action} entity for given actionId with all lazy
|
||||
* attributes.
|
||||
* Get the {@link Action} entity for given actionId with all lazy attributes.
|
||||
*
|
||||
* @param actionId to be id of the action
|
||||
* @return the corresponding {@link Action}
|
||||
@@ -159,8 +136,7 @@ public interface ControllerManagement {
|
||||
Optional<Action> findActionWithDetails(long actionId);
|
||||
|
||||
/**
|
||||
* Retrieves all the {@link ActionStatus} entries of the given
|
||||
* {@link Action}.
|
||||
* Retrieves all the {@link ActionStatus} entries of the given {@link Action}.
|
||||
*
|
||||
* @param pageReq pagination parameter
|
||||
* @param actionId to be filtered on
|
||||
@@ -171,11 +147,8 @@ public interface ControllerManagement {
|
||||
Page<ActionStatus> findActionStatusByAction(@NotNull Pageable pageReq, long actionId);
|
||||
|
||||
/**
|
||||
* Register new target in the repository (plug-and-play) and in case it
|
||||
* already exists updates {@link Target#getAddress()} and
|
||||
* {@link Target#getLastTargetQuery()} and switches if
|
||||
* {@link TargetUpdateStatus#UNKNOWN} to
|
||||
* {@link TargetUpdateStatus#REGISTERED}.
|
||||
* Register new target in the repository (plug-and-play) and in case it already exists updates {@link Target#getAddress()} and
|
||||
* {@link Target#getLastTargetQuery()} and switches if {@link TargetUpdateStatus#UNKNOWN} to {@link TargetUpdateStatus#REGISTERED}.
|
||||
*
|
||||
* @param controllerId reference
|
||||
* @param address the client IP address of the target, might be {@code null}
|
||||
@@ -199,16 +172,13 @@ public interface ControllerManagement {
|
||||
* @return target reference
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
|
||||
Target findOrRegisterTargetIfItDoesNotExist(@NotEmpty String controllerId, @NotNull URI address, String name,
|
||||
String type);
|
||||
Target findOrRegisterTargetIfItDoesNotExist(@NotEmpty String controllerId, @NotNull URI address, String name, String type);
|
||||
|
||||
/**
|
||||
* Retrieves last {@link Action} for a download of an artifact of given
|
||||
* module and target if exists and is not canceled.
|
||||
* Retrieves last {@link Action} for a download of an artifact of given module and target if exists and is not canceled.
|
||||
*
|
||||
* @param controllerId to look for
|
||||
* @param moduleId of the the {@link SoftwareModule} that should be assigned to
|
||||
* the target
|
||||
* @param moduleId of the {@link SoftwareModule} that should be assigned to the target
|
||||
* @return last {@link Action} for given combination
|
||||
* @throws EntityNotFoundException if target with given ID does not exist
|
||||
*/
|
||||
@@ -216,8 +186,7 @@ public interface ControllerManagement {
|
||||
Optional<Action> getActionForDownloadByTargetAndSoftwareModule(@NotEmpty String controllerId, long moduleId);
|
||||
|
||||
/**
|
||||
* Returns configured polling interval at which the controller polls hawkBit
|
||||
* server.
|
||||
* Returns configured polling interval at which the controller polls hawkBit server.
|
||||
*
|
||||
* @return current {@link TenantConfigurationKey#POLLING_TIME_INTERVAL}.
|
||||
*/
|
||||
@@ -233,44 +202,33 @@ public interface ControllerManagement {
|
||||
String getMinPollingTime();
|
||||
|
||||
/**
|
||||
* Returns the count to be used for reducing polling interval while calling
|
||||
* {@link ControllerManagement#getPollingTimeForAction(long)}.
|
||||
* Returns the count to be used for reducing polling interval while calling {@link ControllerManagement#getPollingTimeForAction(long)}.
|
||||
*
|
||||
* @return configured value of
|
||||
* {@link TenantConfigurationKey#MAINTENANCE_WINDOW_POLL_COUNT}.
|
||||
* @return configured value of {@link TenantConfigurationKey#MAINTENANCE_WINDOW_POLL_COUNT}.
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
|
||||
int getMaintenanceWindowPollCount();
|
||||
|
||||
/**
|
||||
* Returns polling time based on the maintenance window for an action.
|
||||
* Server will reduce the polling interval as the start time for maintenance
|
||||
* window approaches, so that at least these many attempts are made between
|
||||
* current polling until start of maintenance window. Poll time keeps
|
||||
* reducing with MinPollingTime as lower limit
|
||||
* {@link TenantConfigurationKey#MIN_POLLING_TIME_INTERVAL}. After the start
|
||||
* of maintenance window, it resets to default
|
||||
* {@link TenantConfigurationKey#POLLING_TIME_INTERVAL}.
|
||||
* Returns polling time based on the maintenance window for an action. Server will reduce the polling interval as the start time for
|
||||
* maintenance window approaches, so that at least these many attempts are made between current polling until start of maintenance window.
|
||||
* Poll time keeps reducing with MinPollingTime as lower limit {@link TenantConfigurationKey#MIN_POLLING_TIME_INTERVAL}. After the start
|
||||
* of maintenance window, it resets to default {@link TenantConfigurationKey#POLLING_TIME_INTERVAL}.
|
||||
*
|
||||
* @param actionId id the {@link Action} for which polling time is calculated
|
||||
* based on it having maintenance window or not
|
||||
* @param actionId id the {@link Action} for which polling time is calculated based on it having maintenance window or not
|
||||
* @return current {@link TenantConfigurationKey#POLLING_TIME_INTERVAL}.
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
|
||||
String getPollingTimeForAction(long actionId);
|
||||
|
||||
/**
|
||||
* Checks if a given target has currently or has even been assigned to the
|
||||
* given artifact through the action history list. This can e.g. indicate if
|
||||
* a target is allowed to download a given artifact because it has currently
|
||||
* assigned or had ever been assigned to the target and so it's visible to a
|
||||
* specific target e.g. for downloading.
|
||||
* Checks if a given target has currently or has even been assigned to the given artifact through the action history list. This can e.g.
|
||||
* indicate if a target is allowed to download a given artifact because it has currently assigned or had ever been assigned to the target
|
||||
* and so it's visible to a specific target e.g. for downloading.
|
||||
*
|
||||
* @param controllerId the ID of the target to check
|
||||
* @param sha1Hash of the artifact to verify if the given target had even been
|
||||
* assigned to
|
||||
* @return {@code true} if the given target has currently or had ever a
|
||||
* relation to the given artifact through the action history,
|
||||
* @param sha1Hash of the artifact to verify if the given target had even been assigned to
|
||||
* @return {@code true} if the given target has currently or had ever a relation to the given artifact through the action history,
|
||||
* otherwise {@code false}
|
||||
* @throws EntityNotFoundException if target with given ID does not exist
|
||||
*/
|
||||
@@ -278,17 +236,13 @@ public interface ControllerManagement {
|
||||
boolean hasTargetArtifactAssigned(@NotEmpty String controllerId, @NotEmpty String sha1Hash);
|
||||
|
||||
/**
|
||||
* Checks if a given target has currently or has even been assigned to the
|
||||
* given artifact through the action history list. This can e.g. indicate if
|
||||
* a target is allowed to download a given artifact because it has currently
|
||||
* assigned or had ever been assigned to the target and so it's visible to a
|
||||
* specific target e.g. for downloading.
|
||||
* Checks if a given target has currently or has even been assigned to the given artifact through the action history list. This can e.g.
|
||||
* indicate if a target is allowed to download a given artifact because it has currently assigned or had ever been assigned to the target
|
||||
* and so it's visible to a specific target e.g. for downloading.
|
||||
*
|
||||
* @param targetId the ID of the target to check
|
||||
* @param sha1Hash of the artifact to verify if the given target had even been
|
||||
* assigned to
|
||||
* @return {@code true} if the given target has currently or had ever a
|
||||
* relation to the given artifact through the action history,
|
||||
* @param sha1Hash of the artifact to verify if the given target had even been assigned to
|
||||
* @return {@code true} if the given target has currently or had ever a relation to the given artifact through the action history,
|
||||
* otherwise {@code false}
|
||||
* @throws EntityNotFoundException if target with given ID does not exist
|
||||
*/
|
||||
@@ -296,21 +250,18 @@ public interface ControllerManagement {
|
||||
boolean hasTargetArtifactAssigned(long targetId, @NotEmpty String sha1Hash);
|
||||
|
||||
/**
|
||||
* Registers retrieved status for given {@link Target} and {@link Action} if
|
||||
* it does not exist yet.
|
||||
* Registers retrieved status for given {@link Target} and {@link Action} if it does not exist yet.
|
||||
*
|
||||
* @param actionId to the handle status for
|
||||
* @param message for the status
|
||||
* @return the update action in case the status has been changed to
|
||||
* {@link Status#RETRIEVED}
|
||||
* @return the update action in case the status has been changed to {@link Status#RETRIEVED}
|
||||
* @throws EntityNotFoundException if action with given ID does not exist
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
|
||||
Action registerRetrieved(long actionId, String message);
|
||||
|
||||
/**
|
||||
* Updates attributes of the controller according to the given
|
||||
* {@link UpdateMode}.
|
||||
* Updates attributes of the controller according to the given {@link UpdateMode}.
|
||||
*
|
||||
* @param controllerId to update
|
||||
* @param attributes to insert
|
||||
@@ -324,47 +275,37 @@ public interface ControllerManagement {
|
||||
Target updateControllerAttributes(@NotEmpty String controllerId, @NotNull Map<String, String> attributes, UpdateMode mode);
|
||||
|
||||
/**
|
||||
* Finds {@link Target} based on given controller ID returns found Target
|
||||
* without details, i.e. NO {@link Target#getTags()} and
|
||||
* {@link Target#getActions()} possible.
|
||||
* Finds {@link Target} based on given controller ID returns found Target without details, i.e.
|
||||
* NO {@link Target#getTags()} and {@link Target#getActions()} possible.
|
||||
*
|
||||
* @param controllerId to look for.
|
||||
* @return {@link Target} or {@code null} if it does not exist
|
||||
* @see Target#getControllerId()
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER + SpringEvalExpressions.HAS_AUTH_OR
|
||||
+ SpringEvalExpressions.IS_SYSTEM_CODE)
|
||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER + SpringEvalExpressions.HAS_AUTH_OR + SpringEvalExpressions.IS_SYSTEM_CODE)
|
||||
Optional<Target> getByControllerId(@NotEmpty String controllerId);
|
||||
|
||||
/**
|
||||
* Finds {@link Target} based on given ID returns found Target without
|
||||
* details, i.e. NO {@link Target#getTags()} and {@link Target#getActions()}
|
||||
* Finds {@link Target} based on given ID returns found Target without details, i.e.
|
||||
* NO {@link Target#getTags()} and {@link Target#getActions()}
|
||||
* possible.
|
||||
*
|
||||
* @param targetId to look for.
|
||||
* @return {@link Target} or {@code null} if it does not exist
|
||||
* @see Target#getId()
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER + SpringEvalExpressions.HAS_AUTH_OR
|
||||
+ SpringEvalExpressions.IS_SYSTEM_CODE)
|
||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER + SpringEvalExpressions.HAS_AUTH_OR + SpringEvalExpressions.IS_SYSTEM_CODE)
|
||||
Optional<Target> get(long targetId);
|
||||
|
||||
/**
|
||||
* Retrieves the specified number of messages from action history of the
|
||||
* given {@link Action} based on messageCount. Regardless of the value of
|
||||
* messageCount, in order to restrict resource utilisation by controllers,
|
||||
* maximum number of messages that are retrieved from database is limited by
|
||||
* {@link RepositoryConstants#MAX_ACTION_HISTORY_MSG_COUNT}. messageCount
|
||||
* less then zero, retrieves the maximum allowed number of action status
|
||||
* messages from history; messageCount equal zero, does not retrieve any
|
||||
* message; and messageCount larger then zero, retrieves the specified
|
||||
* number of messages, limited by maximum allowed number. A controller sends
|
||||
* the feedback for an {@link ActionStatus} as a list of messages; while
|
||||
* returning the messages, even though the messages from multiple
|
||||
* {@link ActionStatus} are retrieved in descending order by the reported
|
||||
* time ({@link ActionStatus#getOccurredAt()}), i.e. latest ActionStatus
|
||||
* first, the sub-ordering of messages from within single
|
||||
* {@link ActionStatus} is unspecified.
|
||||
* Retrieves the specified number of messages from action history of the given {@link Action} based on messageCount. Regardless of the
|
||||
* value of messageCount, in order to restrict resource utilisation by controllers, maximum number of messages that are retrieved from
|
||||
* database is limited by {@link RepositoryConstants#MAX_ACTION_HISTORY_MSG_COUNT}. messageCount less than zero, retrieves the maximum
|
||||
* allowed number of action status messages from history; messageCount equal zero, does not retrieve any message; and messageCount larger
|
||||
* than zero, retrieves the specified number of messages, limited by maximum allowed number. A controller sends the feedback for an
|
||||
* {@link ActionStatus} as a list of messages; while returning the messages, even though the messages from multiple {@link ActionStatus}
|
||||
* are retrieved in descending order by the reported time ({@link ActionStatus#getOccurredAt()}), i.e. latest ActionStatus first, the
|
||||
* sub-ordering of messages from within single {@link ActionStatus} is unspecified.
|
||||
*
|
||||
* @param actionId to be filtered on
|
||||
* @param messageCount is the number of messages to return from history
|
||||
@@ -374,10 +315,8 @@ public interface ControllerManagement {
|
||||
List<String> getActionHistoryMessages(long actionId, int messageCount);
|
||||
|
||||
/**
|
||||
* Cancels given {@link Action} for this {@link Target}. However, it might
|
||||
* be possible that the controller will continue to work on the
|
||||
* cancellation. The controller needs to acknowledge or reject the
|
||||
* cancellation using {@link DdiRootController#postCancelActionFeedback}.
|
||||
* Cancels given {@link Action} for this {@link Target}. However, it might be possible that the controller will continue to work on the
|
||||
* cancellation. The controller needs to acknowledge or reject the cancellation using {@link DdiRootController#postCancelActionFeedback}.
|
||||
*
|
||||
* @param actionId to be canceled
|
||||
* @return canceled {@link Action}
|
||||
@@ -425,8 +364,7 @@ public interface ControllerManagement {
|
||||
* Activate auto confirmation for a given controllerId
|
||||
*
|
||||
* @param controllerId to activate auto-confirmation on
|
||||
* @param initiator can be set optionally (fallback is the current acting security
|
||||
* user)
|
||||
* @param initiator can be set optionally (fallback is the current acting security user)
|
||||
* @param remark (optional) remark
|
||||
* @return the persisted {@link AutoConfirmationStatus}
|
||||
*/
|
||||
@@ -452,4 +390,4 @@ public interface ControllerManagement {
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
|
||||
boolean updateOfflineAssignedVersion(@NotEmpty String controllerId, String distributionName, String version);
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
@@ -173,8 +174,7 @@ public interface SoftwareModuleManagement
|
||||
* @throws EntityNotFoundException if software module with given ID does not exist
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
|
||||
Page<SoftwareModuleMetadata> findMetaDataBySoftwareModuleIdAndTargetVisible(@NotNull Pageable pageable,
|
||||
long id);
|
||||
Page<SoftwareModuleMetadata> findMetaDataBySoftwareModuleIdAndTargetVisible(@NotNull Pageable pageable, long id);
|
||||
|
||||
/**
|
||||
* Finds all meta data by the given software module id.
|
||||
@@ -190,8 +190,7 @@ public interface SoftwareModuleManagement
|
||||
* @throws EntityNotFoundException if software module with given ID does not exist
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
|
||||
Page<SoftwareModuleMetadata> findMetaDataByRsql(@NotNull Pageable pageable, long id,
|
||||
@NotNull String rsqlParam);
|
||||
Page<SoftwareModuleMetadata> findMetaDataByRsql(@NotNull Pageable pageable, long id, @NotNull String rsqlParam);
|
||||
|
||||
/**
|
||||
* Retrieves the {@link SoftwareModule}s by their {@link SoftwareModuleType}
|
||||
@@ -235,4 +234,7 @@ public interface SoftwareModuleManagement
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
|
||||
SoftwareModuleMetadata updateMetaData(@NotNull @Valid SoftwareModuleMetadataUpdate update);
|
||||
}
|
||||
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
|
||||
Map<Long, List<SoftwareModuleMetadata>> findMetaDataBySoftwareModuleIdsAndTargetVisible(Collection<Long> moduleIds);
|
||||
}
|
||||
@@ -29,8 +29,7 @@ public interface BaseEntity extends Serializable, Identifiable<Long> {
|
||||
String getCreatedBy();
|
||||
|
||||
/**
|
||||
* @return time in {@link TimeUnit#MILLISECONDS} when the {@link BaseEntity}
|
||||
* was created.
|
||||
* @return time in {@link TimeUnit#MILLISECONDS} when the {@link BaseEntity} was created.
|
||||
*/
|
||||
long getCreatedAt();
|
||||
|
||||
|
||||
@@ -13,10 +13,9 @@ import static org.eclipse.hawkbit.repository.model.Action.ActionType.DOWNLOAD_ON
|
||||
import static org.eclipse.hawkbit.repository.model.Action.Status.ERROR;
|
||||
import static org.eclipse.hawkbit.repository.model.Action.Status.FINISHED;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||
@@ -25,7 +24,6 @@ import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.jpa.builder.JpaActionStatusCreate;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction_;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.ActionStatusRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.specifications.ActionSpecifications;
|
||||
@@ -34,7 +32,7 @@ import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
|
||||
/**
|
||||
* Implements utility methods for managing {@link Action}s
|
||||
@@ -104,32 +102,19 @@ public class JpaActionManagement {
|
||||
quotaManagement.getMaxMessagesPerActionStatus(), "Message", ActionStatus.class.getSimpleName(), null);
|
||||
}
|
||||
|
||||
List<Action> findActiveActionsWithHighestWeightConsideringDefault(final String controllerId,
|
||||
final int maxActionCount) {
|
||||
final List<Action> actions = new ArrayList<>();
|
||||
actions.addAll(
|
||||
actionRepository
|
||||
.findAll(
|
||||
ActionSpecifications.byTargetControllerIdAndActiveAndWeightIsNullFetchDS(controllerId, false),
|
||||
PageRequest.of(
|
||||
0, maxActionCount,
|
||||
Sort.by(
|
||||
Sort.Order.desc(JpaAction_.WEIGHT),
|
||||
Sort.Order.asc(JpaAction_.ID))))
|
||||
.getContent());
|
||||
|
||||
actions.addAll(
|
||||
actionRepository
|
||||
.findAll(
|
||||
ActionSpecifications.byTargetControllerIdAndActiveAndWeightIsNullFetchDS(controllerId, true),
|
||||
PageRequest.of(
|
||||
0, maxActionCount,
|
||||
Sort.by(
|
||||
Sort.Order.asc(JpaAction_.ID))))
|
||||
.getContent());
|
||||
final Comparator<Action> actionImportance = Comparator.comparingInt(this::getWeightConsideringDefault)
|
||||
.reversed().thenComparing(Action::getId);
|
||||
return actions.stream().sorted(actionImportance).limit(maxActionCount).collect(Collectors.toList());
|
||||
protected List<Action> findActiveActionsWithHighestWeightConsideringDefault(final String controllerId, final int maxActionCount) {
|
||||
final Pageable pageable = PageRequest.of(0, maxActionCount);
|
||||
return Stream.concat(
|
||||
// get the highest actions with weight
|
||||
actionRepository
|
||||
.findFetchDsByTarget_ControllerIdAndActiveIsTrueAndWeightNotNullOrderByWeightDescIdAsc(controllerId, pageable)
|
||||
.stream(),
|
||||
// get the oldest actions without weight
|
||||
actionRepository.findFetchDsByTarget_ControllerIdAndActiveIsTrueAndWeightIsNullOrderByIdAsc(controllerId, pageable)
|
||||
.stream())
|
||||
.sorted(Comparator.comparingInt(this::getWeightConsideringDefault).reversed().thenComparing(Action::getId))
|
||||
.limit(maxActionCount)
|
||||
.toList();
|
||||
}
|
||||
|
||||
private static boolean isIntermediateStatus(final JpaActionStatus actionStatus) {
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.time.temporal.ChronoUnit;
|
||||
import java.time.temporal.TemporalUnit;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -32,6 +33,7 @@ import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.Query;
|
||||
@@ -250,8 +252,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
||||
@Override
|
||||
public Map<Long, List<SoftwareModuleMetadata>> findTargetVisibleMetaDataBySoftwareModuleId(final Collection<Long> moduleId) {
|
||||
return softwareModuleMetadataRepository
|
||||
.findBySoftwareModuleIdInAndTargetVisible(
|
||||
PageRequest.of(0, RepositoryConstants.MAX_META_DATA_COUNT), moduleId, true)
|
||||
.findBySoftwareModuleIdInAndTargetVisible(moduleId, true, PageRequest.of(0, RepositoryConstants.MAX_META_DATA_COUNT))
|
||||
.getContent().stream()
|
||||
.collect(Collectors.groupingBy(o -> (Long) o[0], Collectors.mapping(o -> (SoftwareModuleMetadata) o[1], Collectors.toList())));
|
||||
}
|
||||
@@ -280,14 +281,23 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
||||
return addActionStatus((JpaActionStatusCreate) statusCreate);
|
||||
}
|
||||
|
||||
private static final Pageable PAGEABLE_1 = PageRequest.of(0, 1);
|
||||
|
||||
@Override
|
||||
public Optional<Action> findActiveActionWithHighestWeight(final String controllerId) {
|
||||
return findActiveActionsWithHighestWeight(controllerId, 1).stream().findFirst();
|
||||
return Stream.concat(
|
||||
// get the highest action with weight
|
||||
actionRepository
|
||||
.findByTarget_ControllerIdAndActiveIsTrueAndWeightNotNullOrderByWeightDescIdAsc(controllerId, PAGEABLE_1)
|
||||
.stream(),
|
||||
// get the oldest action without weight
|
||||
actionRepository.findByTarget_ControllerIdAndActiveIsTrueAndWeightIsNullOrderByIdAsc(controllerId, PAGEABLE_1).stream())
|
||||
.min(Comparator.comparingInt(this::getWeightConsideringDefault).reversed().thenComparing(Action::getId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Action> findActiveActionsWithHighestWeight(final String controllerId, final int maxActionCount) {
|
||||
return findActiveActionsWithHighestWeightConsideringDefault(controllerId, maxActionCount);
|
||||
return super.findActiveActionsWithHighestWeightConsideringDefault(controllerId, maxActionCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -453,7 +463,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
||||
|
||||
@Override
|
||||
public Optional<Target> get(final long targetId) {
|
||||
return targetRepository.findById(targetId).map(t -> (Target) t);
|
||||
return targetRepository.findById(targetId).map(Target.class::cast);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -463,18 +473,16 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// For negative and large value of messageCount, limit the number of
|
||||
// messages.
|
||||
// For negative and large value of messageCount, limit the number of messages.
|
||||
final int limit = messageCount < 0 || messageCount >= RepositoryConstants.MAX_ACTION_HISTORY_MSG_COUNT
|
||||
? RepositoryConstants.MAX_ACTION_HISTORY_MSG_COUNT
|
||||
: messageCount;
|
||||
|
||||
final PageRequest pageable = PageRequest.of(0, limit, Sort.by(Direction.DESC, "occurredAt"));
|
||||
final Page<String> messages = actionStatusRepository.findMessagesByActionIdAndMessageNotLike(pageable, actionId,
|
||||
RepositoryConstants.SERVER_MESSAGE_PREFIX + "%");
|
||||
final Page<String> messages = actionStatusRepository.findMessagesByActionIdAndMessageNotLike(
|
||||
actionId, RepositoryConstants.SERVER_MESSAGE_PREFIX + "%", pageable);
|
||||
|
||||
log.debug("Retrieved {} message(s) from action history for action {}.", messages.getNumberOfElements(),
|
||||
actionId);
|
||||
log.debug("Retrieved {} message(s) from action history for action {}.", messages.getNumberOfElements(), actionId);
|
||||
|
||||
return messages.getContent();
|
||||
}
|
||||
|
||||
@@ -304,7 +304,7 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
|
||||
|
||||
if (!rolloutGroupIds.isEmpty()) {
|
||||
final List<TotalTargetCountActionStatus> resultList = actionRepository
|
||||
.getStatusCountByRolloutGroupId(rolloutGroupIds);
|
||||
.getStatusCountByRolloutGroupIds(rolloutGroupIds);
|
||||
final Map<Long, List<TotalTargetCountActionStatus>> fromDb = resultList.stream()
|
||||
.collect(Collectors.groupingBy(TotalTargetCountActionStatus::getId));
|
||||
|
||||
|
||||
@@ -711,8 +711,7 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (!rolloutIds.isEmpty()) {
|
||||
final List<TotalTargetCountActionStatus> resultList = actionRepository
|
||||
.getStatusCountByRolloutId(rolloutIds);
|
||||
final List<TotalTargetCountActionStatus> resultList = actionRepository.getStatusCountByRolloutIds(rolloutIds);
|
||||
final Map<Long, List<TotalTargetCountActionStatus>> fromDb = resultList.stream()
|
||||
.collect(Collectors.groupingBy(TotalTargetCountActionStatus::getId));
|
||||
|
||||
|
||||
@@ -411,7 +411,7 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
|
||||
assertSoftwareModuleExists(id);
|
||||
|
||||
return JpaManagementHelper.convertPage(softwareModuleMetadataRepository.findBySoftwareModuleIdAndTargetVisible(
|
||||
PageRequest.of(0, RepositoryConstants.MAX_META_DATA_COUNT), id, true), pageable);
|
||||
id, true, PageRequest.of(0, RepositoryConstants.MAX_META_DATA_COUNT)), pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -486,6 +486,14 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
|
||||
return softwareModuleMetadataRepository.save(metadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Long, List<SoftwareModuleMetadata>> findMetaDataBySoftwareModuleIdsAndTargetVisible(final Collection<Long> moduleIds) {
|
||||
return softwareModuleMetadataRepository
|
||||
.findBySoftwareModuleIdInAndTargetVisible(moduleIds, true, PageRequest.of(0, RepositoryConstants.MAX_META_DATA_COUNT))
|
||||
.getContent().stream()
|
||||
.collect(Collectors.groupingBy(o -> (Long) o[0], Collectors.mapping(o -> (SoftwareModuleMetadata) o[1], Collectors.toList())));
|
||||
}
|
||||
|
||||
private static Stream<JpaSoftwareModuleMetadataCreate> createJpaMetadataCreateStream(
|
||||
final Collection<SoftwareModuleMetadataCreate> create) {
|
||||
return create.stream().map(JpaSoftwareModuleMetadataCreate.class::cast);
|
||||
|
||||
@@ -83,15 +83,14 @@ public class JpaActionStatus extends AbstractJpaTenantAwareBaseEntity implements
|
||||
@CollectionTable(
|
||||
name = "sp_action_status_messages",
|
||||
joinColumns = @JoinColumn(
|
||||
name = "action_status_id", insertable = false, updatable = false, nullable = false,
|
||||
name = "action_status_id", nullable = false,
|
||||
foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_stat_msg_act_stat")),
|
||||
indexes = {
|
||||
@Index(name = "sp_idx_action_status_msgs_01", columnList = "action_status_id") })
|
||||
@Column(name = "detail_message", length = MESSAGE_ENTRY_LENGTH, nullable = false, insertable = false, updatable = false)
|
||||
indexes = { @Index(name = "sp_idx_action_status_msgs_01", columnList = "action_status_id") })
|
||||
@Column(name = "detail_message", length = MESSAGE_ENTRY_LENGTH, nullable = false)
|
||||
private List<String> messages;
|
||||
|
||||
@Setter
|
||||
@Column(name = "code", nullable = true, updatable = false)
|
||||
@Column(name = "code", updatable = false)
|
||||
private Integer code;
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,6 +16,7 @@ import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import jakarta.persistence.CascadeType;
|
||||
@@ -69,6 +70,8 @@ import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.persistence.descriptors.DescriptorEvent;
|
||||
import org.eclipse.persistence.descriptors.DescriptorEventAdapter;
|
||||
import org.eclipse.persistence.queries.UpdateObjectQuery;
|
||||
import org.hibernate.event.spi.PreUpdateEvent;
|
||||
import org.hibernate.event.spi.PreUpdateEventListener;
|
||||
|
||||
/**
|
||||
* JPA implementation of {@link Target}.
|
||||
@@ -174,12 +177,12 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
|
||||
// no cascade option on an ElementCollection, the target objects are always persisted, merged, removed with their parent.
|
||||
@Getter
|
||||
@ElementCollection
|
||||
@Column(name = "attribute_value", length = Target.CONTROLLER_ATTRIBUTE_VALUE_SIZE)
|
||||
@MapKeyColumn(name = "attribute_key", nullable = false, length = Target.CONTROLLER_ATTRIBUTE_KEY_SIZE)
|
||||
@CollectionTable(
|
||||
name = "sp_target_attributes",
|
||||
joinColumns = { @JoinColumn(name = "target_id", nullable = false, insertable = false, updatable = false) },
|
||||
joinColumns = { @JoinColumn(name = "target_id", nullable = false) },
|
||||
foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_attrib_target"))
|
||||
@Column(name = "attribute_value", length = Target.CONTROLLER_ATTRIBUTE_VALUE_SIZE)
|
||||
@MapKeyColumn(name = "attribute_key", length = Target.CONTROLLER_ATTRIBUTE_KEY_SIZE)
|
||||
private Map<String, String> controllerAttributes;
|
||||
|
||||
@OneToMany(mappedBy = "target", fetch = FetchType.LAZY, cascade = { CascadeType.REMOVE }, targetEntity = JpaTargetMetadata.class)
|
||||
@@ -331,7 +334,7 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
|
||||
/**
|
||||
* Listens to updates on {@link JpaTarget} entities, Filtering out updates that only change the "lastTargetQuery" or "address" fields.
|
||||
*/
|
||||
public static class EntityPropertyChangeListener extends DescriptorEventAdapter {
|
||||
public static class EntityPropertyChangeListener extends DescriptorEventAdapter implements PreUpdateEventListener {
|
||||
|
||||
private static final List<String> TARGET_UPDATE_EVENT_IGNORE_FIELDS = List.of(
|
||||
"lastTargetQuery", "address", // actual to be skipped
|
||||
@@ -346,5 +349,21 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
|
||||
doNotify(() -> ((EventAwareEntity) object).fireUpdateEvent());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreUpdate(final PreUpdateEvent event) {
|
||||
final Object[] oldState = event.getOldState();
|
||||
final Object[] newState = event.getState();
|
||||
for (int i = 0; i < newState.length; i++) {
|
||||
if (!Objects.equals(oldState[i], newState[i])) {
|
||||
final String attribute = event.getPersister().getAttributeMapping(i).getAttributeName();
|
||||
if (!TARGET_UPDATE_EVENT_IGNORE_FIELDS.contains(attribute)) {
|
||||
doNotify(() -> ((EventAwareEntity) event.getEntity()).fireUpdateEvent());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -52,15 +52,22 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction> {
|
||||
* @param targetId the action belongs to
|
||||
* @param dsId of the ds that is assigned to the target
|
||||
* @param status of the action
|
||||
* @return action if there is one with assigned target and assigned
|
||||
* {@link DistributionSet}.
|
||||
* @return action if there is one with assigned target and assigned {@link DistributionSet}.
|
||||
*/
|
||||
Optional<Action> findFirstByTargetIdAndDistributionSetIdAndStatusOrderByIdDesc(@Param("target") long targetId,
|
||||
@Param("ds") Long dsId, @Param("status") Action.Status status);
|
||||
Optional<Action> findFirstByTargetIdAndDistributionSetIdAndStatusOrderByIdDesc(
|
||||
@Param("target") long targetId, @Param("ds") Long dsId, @Param("status") Action.Status status);
|
||||
|
||||
List<Action> findByTarget_ControllerIdAndActiveIsTrueAndWeightIsNullOrderByIdAsc(String controllerId, Pageable pageable);
|
||||
List<Action> findByTarget_ControllerIdAndActiveIsTrueAndWeightNotNullOrderByWeightDescIdAsc(String controllerId, Pageable pageable);
|
||||
|
||||
@EntityGraph(value = "Action.ds", type = EntityGraphType.LOAD)
|
||||
List<Action> findFetchDsByTarget_ControllerIdAndActiveIsTrueAndWeightIsNullOrderByIdAsc(String controllerId, Pageable pageable);
|
||||
@EntityGraph(value = "Action.ds", type = EntityGraphType.LOAD)
|
||||
List<Action> findFetchDsByTarget_ControllerIdAndActiveIsTrueAndWeightNotNullOrderByWeightDescIdAsc(String controllerId, Pageable pageable);
|
||||
|
||||
/**
|
||||
* Switches the status of actions from one specific status into another, only if
|
||||
* the actions are in a specific status. This should be a atomic operation.
|
||||
* Switches the status of actions from one specific status into another, only if the actions are in a specific status. This should be
|
||||
* an atomic operation.
|
||||
* <p/>
|
||||
* No access control applied
|
||||
*
|
||||
@@ -72,7 +79,8 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction> {
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Query("UPDATE JpaAction a SET a.status = :statusToSet WHERE a.target.id IN :targetsIds AND a.active = :active AND a.status = :currentStatus AND a.distributionSet.requiredMigrationStep = false")
|
||||
void switchStatus(@Param("statusToSet") Action.Status statusToSet, @Param("targetsIds") List<Long> targetIds,
|
||||
void switchStatus(
|
||||
@Param("statusToSet") Action.Status statusToSet, @Param("targetsIds") List<Long> targetIds,
|
||||
@Param("active") boolean active, @Param("currentStatus") Action.Status currentStatus);
|
||||
|
||||
/**
|
||||
@@ -124,8 +132,7 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction> {
|
||||
Long countByDistributionSetIdAndActiveIsTrue(Long distributionSet);
|
||||
|
||||
/**
|
||||
* Counts all active {@link Action}s referring to the given DistributionSet
|
||||
* that are not in a given state.
|
||||
* Counts all active {@link Action}s referring to the given DistributionSet that are not in a given state.
|
||||
* <p/>
|
||||
* No access control applied
|
||||
*
|
||||
@@ -136,43 +143,39 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction> {
|
||||
Long countByDistributionSetIdAndActiveIsTrueAndStatusIsNot(Long distributionSet, Status status);
|
||||
|
||||
/**
|
||||
* Counts all actions referring to a given rollout and rolloutgroup which
|
||||
* are currently not in the given status. An in-clause statement does not
|
||||
* work with the spring-data, so this is specific usecase regarding to the
|
||||
* rollout-management to find out actions which are not in specific states.
|
||||
* Counts all actions referring to a given rollout and rolloutgroup which are currently not in the given status. An in-clause statement
|
||||
* does not work with the spring-data, so this is specific usecase regarding the rollout-management to find out actions which are not in
|
||||
* specific states.
|
||||
* <p/>
|
||||
* No access control applied
|
||||
*
|
||||
* @param rollout the rollout the actions are belong to
|
||||
* @param rolloutGroup the rolloutgroup the actions are belong to
|
||||
* @param rolloutGroup the rollout group the actions are belong to
|
||||
* @param statuses the list of statuses the action should not have
|
||||
* @return the count of actions referring the rollout and rolloutgroup and
|
||||
* are not in given states
|
||||
* @return the count of actions referring the rollout and rollout group and are not in given states
|
||||
*/
|
||||
Long countByRolloutAndRolloutGroupAndStatusNotIn(JpaRollout rollout, JpaRolloutGroup rolloutGroup,
|
||||
List<Status> statuses);
|
||||
Long countByRolloutAndRolloutGroupAndStatusNotIn(JpaRollout rollout, JpaRolloutGroup rolloutGroup, List<Status> statuses);
|
||||
|
||||
/**
|
||||
* Counts all actions referring to a given rollout and rolloutgroup.
|
||||
* Counts all actions referring to a given rollout and rollout group.
|
||||
* <p/>
|
||||
* No access control applied
|
||||
*
|
||||
* @param rollout the rollout the actions belong to
|
||||
* @param rolloutGroup the rolloutgroup the actions belong to
|
||||
* @return the count of actions referring to a rollout and rolloutgroup
|
||||
* @param rolloutGroup the rollout group the actions belong to
|
||||
* @return the count of actions referring to a rollout and rollout group
|
||||
*/
|
||||
Long countByRolloutAndRolloutGroup(JpaRollout rollout, JpaRolloutGroup rolloutGroup);
|
||||
|
||||
/**
|
||||
* Counts all actions referring to a given rollout, rolloutgroup and status.
|
||||
* Counts all actions referring to a given rollout, rollout group and status.
|
||||
* <p/>
|
||||
* No access control applied
|
||||
*
|
||||
* @param rolloutId the ID of rollout the actions belong to
|
||||
* @param rolloutGroupId the ID rolloutgroup the actions belong to
|
||||
* @param rolloutGroupId the ID rollout group the actions belong to
|
||||
* @param status the status the actions should have
|
||||
* @return the count of actions referring to a rollout, rolloutgroup and are
|
||||
* in a given status
|
||||
* @return the count of actions referring to a rollout, rollout group and are in a given status
|
||||
*/
|
||||
Long countByRolloutIdAndRolloutGroupIdAndStatus(Long rolloutId, Long rolloutGroupId, Action.Status status);
|
||||
|
||||
@@ -189,50 +192,43 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction> {
|
||||
Long countByRolloutIdAndStatus(Long rolloutId, Action.Status status);
|
||||
|
||||
/**
|
||||
* Returns {@code true} if actions for the given rollout exists, otherwise
|
||||
* {@code false}
|
||||
* Returns {@code true} if actions for the given rollout exists, otherwise {@code false}
|
||||
* <p/>
|
||||
* No access control applied
|
||||
*
|
||||
* @param rolloutId the ID of the rollout the actions belong to
|
||||
* @return {@code true} if actions for the given rollout exists, otherwise
|
||||
* {@code false}
|
||||
* @return {@code true} if actions for the given rollout exists, otherwise {@code false}
|
||||
*/
|
||||
@Query("SELECT CASE WHEN COUNT(a)>0 THEN 'true' ELSE 'false' END FROM JpaAction a WHERE a.rollout.id=:rolloutId")
|
||||
boolean existsByRolloutId(@Param("rolloutId") Long rolloutId);
|
||||
|
||||
/**
|
||||
* Returns {@code true} if actions for the given rollout exists, otherwise
|
||||
* {@code false}
|
||||
* Returns {@code true} if actions for the given rollout exists, otherwise {@code false}
|
||||
* <p/>
|
||||
* No access control applied
|
||||
*
|
||||
* @param rolloutId the ID of the rollout the actions belong to
|
||||
* @param status the action is not to be in
|
||||
* @return {@code true} if actions for the given rollout exists, otherwise
|
||||
* {@code false}
|
||||
* @return {@code true} if actions for the given rollout exists, otherwise {@code false}
|
||||
*/
|
||||
@Query("SELECT CASE WHEN COUNT(a)>0 THEN 'true' ELSE 'false' END FROM JpaAction a WHERE a.rollout.id=:rolloutId AND a.status != :status")
|
||||
boolean existsByRolloutIdAndStatusNotIn(@Param("rolloutId") Long rolloutId, @Param("status") Status status);
|
||||
|
||||
/**
|
||||
* Retrieving all actions referring to a given rollout with a specific action as
|
||||
* parent reference and a specific status.
|
||||
* Retrieving all actions referring to a given rollout with a specific action as parent reference and a specific status.
|
||||
* <p/>
|
||||
* Finding all actions of a specific rolloutgroup parent relation.
|
||||
* Finding all actions of a specific rollout group parent relation.
|
||||
* <p/>
|
||||
* No access control applied
|
||||
*
|
||||
* @param pageable page parameters
|
||||
* @param rollout the rollout the actions belong to
|
||||
* @param rolloutGroupParent the parent rolloutgroup the actions should reference
|
||||
* @param rolloutGroupParent the parent rollout group the actions should reference
|
||||
* @param actionStatus the status the actions have
|
||||
* @return the actions referring a specific rollout and a specific parent
|
||||
* rolloutgroup in a specific status
|
||||
* @return the actions referring a specific rollout and a specific parent rolloutgroup in a specific status
|
||||
*/
|
||||
@EntityGraph(attributePaths = { "target", "target.autoConfirmationStatus", "rolloutGroup" }, type = EntityGraphType.LOAD)
|
||||
Page<Action> findByRolloutIdAndRolloutGroupParentIdAndStatus(Pageable pageable, Long rollout,
|
||||
Long rolloutGroupParent, Status actionStatus);
|
||||
Page<Action> findByRolloutIdAndRolloutGroupParentIdAndStatus(Pageable pageable, Long rollout, Long rolloutGroupParent, Status actionStatus);
|
||||
|
||||
/**
|
||||
* Retrieving all actions referring to the first group of a rollout.
|
||||
@@ -242,13 +238,10 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction> {
|
||||
* @param pageable page parameters
|
||||
* @param rollout the rollout the actions belong to
|
||||
* @param actionStatus the status the actions have
|
||||
* @return the actions referring a specific rollout and a specific parent
|
||||
* rolloutgroup in a specific status
|
||||
* @return the actions referring a specific rollout and a specific parent rolloutgroup in a specific status
|
||||
*/
|
||||
@EntityGraph(attributePaths = { "target", "target.autoConfirmationStatus",
|
||||
"rolloutGroup" }, type = EntityGraphType.LOAD)
|
||||
Page<Action> findByRolloutIdAndRolloutGroupParentIsNullAndStatus(Pageable pageable, Long rollout,
|
||||
Status actionStatus);
|
||||
@EntityGraph(attributePaths = { "target", "target.autoConfirmationStatus", "rolloutGroup" }, type = EntityGraphType.LOAD)
|
||||
Page<Action> findByRolloutIdAndRolloutGroupParentIsNullAndStatus(Pageable pageable, Long rollout, Status actionStatus);
|
||||
|
||||
/**
|
||||
* Retrieves all actions for a specific rollout and in a specific status.
|
||||
@@ -263,44 +256,40 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction> {
|
||||
Page<JpaAction> findByRolloutIdAndStatus(Pageable pageable, Long rolloutId, Status actionStatus);
|
||||
|
||||
/**
|
||||
* Get list of objects which has details of status and count of targets in
|
||||
* each status in specified rollout.
|
||||
* Get list of objects which has details of status and count of targets in each status in specified rollout.
|
||||
* <p/>
|
||||
* No access control applied
|
||||
*
|
||||
* @param rolloutId id of {@link Rollout}
|
||||
* @return list of objects with status and target count
|
||||
*/
|
||||
@Query("SELECT NEW org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus( a.rollout.id, a.status , COUNT(a.id)) FROM JpaAction a WHERE a.rollout.id IN ?1 GROUP BY a.rollout.id,a.status")
|
||||
List<TotalTargetCountActionStatus> getStatusCountByRolloutId(List<Long> rolloutId);
|
||||
|
||||
/**
|
||||
* Get list of objects which has details of status and count of targets in
|
||||
* each status in specified rollout.
|
||||
* <p/>
|
||||
* No access control applied
|
||||
*
|
||||
* @param rolloutId id of {@link Rollout}
|
||||
* @return list of objects with status and target count
|
||||
*/
|
||||
@Query("SELECT NEW org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus( a.rollout.id, a.status , COUNT(a.id)) FROM JpaAction a WHERE a.rollout.id = ?1 GROUP BY a.rollout.id,a.status")
|
||||
@Query("SELECT NEW org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus(a.rollout.id, a.status, COUNT(a.id)) FROM JpaAction a WHERE a.rollout.id = ?1 GROUP BY a.rollout.id,a.status")
|
||||
List<TotalTargetCountActionStatus> getStatusCountByRolloutId(Long rolloutId);
|
||||
|
||||
/**
|
||||
* Get list of objects which has details of status and count of targets in
|
||||
* each status in specified rollout group.
|
||||
* Get list of objects which has details of status and count of targets in each status in specified rollout.
|
||||
* <p/>
|
||||
* No access control applied
|
||||
*
|
||||
* @param rolloutId id of {@link Rollout}
|
||||
* @return list of objects with status and target count
|
||||
*/
|
||||
@Query("SELECT NEW org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus(a.rollout.id, a.status, COUNT(a.id)) FROM JpaAction a WHERE a.rollout.id IN ?1 GROUP BY a.rollout.id,a.status")
|
||||
List<TotalTargetCountActionStatus> getStatusCountByRolloutIds(List<Long> rolloutId);
|
||||
|
||||
/**
|
||||
* Get list of objects which has details of status and count of targets in each status in specified rollout group.
|
||||
* <p/>
|
||||
* No access control applied
|
||||
*
|
||||
* @param rolloutGroupId id of {@link RolloutGroup}
|
||||
* @return list of objects with status and target count
|
||||
*/
|
||||
@Query("SELECT NEW org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus(a.rolloutGroup.id, a.status , COUNT(a.id)) FROM JpaAction a WHERE a.rolloutGroup.id = ?1 GROUP BY a.rolloutGroup.id, a.status")
|
||||
@Query("SELECT NEW org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus(a.rolloutGroup.id, a.status, COUNT(a.id)) FROM JpaAction a WHERE a.rolloutGroup.id = ?1 GROUP BY a.rolloutGroup.id, a.status")
|
||||
List<TotalTargetCountActionStatus> getStatusCountByRolloutGroupId(Long rolloutGroupId);
|
||||
|
||||
/**
|
||||
* Get list of objects which has details of status and count of targets in
|
||||
* each status in specified rollout group.
|
||||
* Get list of objects which has details of status and count of targets in each status in specified rollout group.
|
||||
* <p/>
|
||||
* No access control applied
|
||||
*
|
||||
@@ -308,7 +297,7 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction> {
|
||||
* @return list of objects with status and target count
|
||||
*/
|
||||
@Query("SELECT NEW org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus(a.rolloutGroup.id, a.status , COUNT(a.id)) FROM JpaAction a WHERE a.rolloutGroup.id IN ?1 GROUP BY a.rolloutGroup.id, a.status")
|
||||
List<TotalTargetCountActionStatus> getStatusCountByRolloutGroupId(List<Long> rolloutGroupId);
|
||||
List<TotalTargetCountActionStatus> getStatusCountByRolloutGroupIds(List<Long> rolloutGroupId);
|
||||
|
||||
/**
|
||||
* Updates the externalRef of an action by its actionId.
|
||||
@@ -331,4 +320,4 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction> {
|
||||
// Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477
|
||||
@Query("DELETE FROM JpaAction a WHERE a.id IN ?1")
|
||||
void deleteByIdIn(Collection<Long> actionIDs);
|
||||
}
|
||||
}
|
||||
@@ -52,13 +52,11 @@ public interface ActionStatusRepository extends BaseEntityRepository<JpaActionSt
|
||||
* <p/>
|
||||
* No access control applied
|
||||
*
|
||||
* @param pageable for page configuration
|
||||
* @param actionId for which to get the status messages
|
||||
* @param filter is the SQL like pattern to use for filtering out or excluding
|
||||
* the messages
|
||||
* @param filter is the SQL like pattern to use for filtering out or excluding the messages
|
||||
* @param pageable for page configuration
|
||||
* @return Page with found status messages.
|
||||
*/
|
||||
@Query("SELECT message FROM JpaActionStatus actionstatus JOIN actionstatus.messages message WHERE actionstatus.action.id = :actionId AND message NOT LIKE :filter")
|
||||
Page<String> findMessagesByActionIdAndMessageNotLike(Pageable pageable, @Param("actionId") Long actionId,
|
||||
@Param("filter") String filter);
|
||||
}
|
||||
Page<String> findMessagesByActionIdAndMessageNotLike(@Param("actionId") Long actionId, @Param("filter") String filter, Pageable pageable);
|
||||
}
|
||||
@@ -133,8 +133,7 @@ public class BaseEntityRepositoryACM<T extends AbstractJpaTenantAwareBaseEntity>
|
||||
@Override
|
||||
public void deleteByTenant(final String tenant) {
|
||||
if (accessController.getAccessRules(AccessController.Operation.DELETE).isPresent()) {
|
||||
throw new InsufficientPermissionException(
|
||||
"DELETE operation has restriction for given context! deleteAll can't be executed!");
|
||||
throw new InsufficientPermissionException("DELETE operation has restriction for given context! deleteAll can't be executed!");
|
||||
}
|
||||
repository.deleteByTenant(tenant);
|
||||
}
|
||||
@@ -185,8 +184,7 @@ public class BaseEntityRepositoryACM<T extends AbstractJpaTenantAwareBaseEntity>
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S extends T, R> R findBy(final Specification<T> spec,
|
||||
final Function<FluentQuery.FetchableFluentQuery<S>, R> queryFunction) {
|
||||
public <S extends T, R> R findBy(final Specification<T> spec, final Function<FluentQuery.FetchableFluentQuery<S>, R> queryFunction) {
|
||||
return repository.findBy(accessController.appendAccessRules(AccessController.Operation.READ, spec), queryFunction);
|
||||
}
|
||||
|
||||
|
||||
@@ -33,40 +33,37 @@ public interface SoftwareModuleMetadataRepository
|
||||
JpaSpecificationExecutor<JpaSoftwareModuleMetadata> {
|
||||
|
||||
/**
|
||||
* Locates the meta data entries that match the given software module ID and
|
||||
* target visibility flag.
|
||||
* Locates the meta-data entries that match the given software module ID and target visibility flag.
|
||||
*
|
||||
* @param page The pagination parameters.
|
||||
* @param moduleId The ID of the software module.
|
||||
* @param targetVisible The target visibility flag.
|
||||
* @return A {@link Page} with the matching meta data entries.
|
||||
* @param page The pagination parameters.
|
||||
* @return A {@link Page} with the matching meta-data entries.
|
||||
*/
|
||||
Page<JpaSoftwareModuleMetadata> findBySoftwareModuleIdAndTargetVisible(Pageable page, Long moduleId,
|
||||
boolean targetVisible);
|
||||
Page<JpaSoftwareModuleMetadata> findBySoftwareModuleIdAndTargetVisible(
|
||||
@Param("moduleId") Long moduleId, @Param("targetVisible") boolean targetVisible, Pageable page);
|
||||
|
||||
/**
|
||||
* Locates the meta data entries that match the given software module IDs
|
||||
* and target visibility flag.
|
||||
* Locates the meta-data entries that match the given software module IDs and target visibility flag.
|
||||
* <p/>
|
||||
* No access control applied
|
||||
*
|
||||
* @param page The pagination parameters.
|
||||
* @param moduleId List of software module IDs.
|
||||
* @param targetVisible The target visibility flag.
|
||||
* @return A {@link Page} with the matching meta data entries.
|
||||
* @param page The pagination parameters.
|
||||
* @return A {@link Page} with the matching meta-data entries.
|
||||
*/
|
||||
@Query("SELECT smd.softwareModule.id, smd FROM JpaSoftwareModuleMetadata smd WHERE smd.softwareModule.id IN :moduleId AND smd.targetVisible = :targetVisible")
|
||||
Page<Object[]> findBySoftwareModuleIdInAndTargetVisible(Pageable page, @Param("moduleId") Collection<Long> moduleId,
|
||||
@Param("targetVisible") boolean targetVisible);
|
||||
Page<Object[]> findBySoftwareModuleIdInAndTargetVisible(
|
||||
@Param("moduleId") Collection<Long> moduleId, @Param("targetVisible") boolean targetVisible, Pageable page);
|
||||
|
||||
/**
|
||||
* Counts the meta data entries that are associated with the addressed
|
||||
* software module.
|
||||
* Counts the meta-data entries that are associated with the addressed software module.
|
||||
* <p/>
|
||||
* No access control applied
|
||||
*
|
||||
* @param moduleId The ID of the software module.
|
||||
* @return The number of meta data entries associated with the software module.
|
||||
* @return The number of meta-data entries associated with the software module.
|
||||
*/
|
||||
long countBySoftwareModuleId(@Param("moduleId") Long moduleId);
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,6 @@ package org.eclipse.hawkbit.repository.jpa.specifications;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.persistence.criteria.Join;
|
||||
import jakarta.persistence.criteria.JoinType;
|
||||
import jakarta.persistence.criteria.ListJoin;
|
||||
import jakarta.persistence.criteria.SetJoin;
|
||||
|
||||
@@ -82,18 +81,13 @@ public final class ActionSpecifications {
|
||||
* Fetches action's distribution set.
|
||||
*
|
||||
* @param controllerId controller id
|
||||
* @param isNull if <code>true</code> return with <code>null</code> weight, otherwise with non-<code>null</code>
|
||||
* @return the matching action s.
|
||||
*/
|
||||
public static Specification<JpaAction> byTargetControllerIdAndActiveAndWeightIsNullFetchDS(final String controllerId,
|
||||
final boolean isNull) {
|
||||
return (root, query, cb) -> {
|
||||
root.fetch(JpaAction_.distributionSet, JoinType.LEFT);
|
||||
return cb.and(
|
||||
public static Specification<JpaAction> byTargetControllerIdAndActive(final String controllerId) {
|
||||
return (root, query, cb) ->
|
||||
cb.and(
|
||||
cb.equal(root.get(JpaAction_.target).get(JpaTarget_.controllerId), controllerId),
|
||||
cb.equal(root.get(JpaAction_.active), true),
|
||||
isNull ? cb.isNull(root.get(JpaAction_.weight)) : cb.isNotNull(root.get(JpaAction_.weight)));
|
||||
};
|
||||
cb.equal(root.get(JpaAction_.active), true));
|
||||
}
|
||||
|
||||
public static Specification<JpaAction> byDistributionSetId(final Long distributionSetId) {
|
||||
@@ -158,4 +152,4 @@ public final class ActionSpecifications {
|
||||
criteriaBuilder.equal(actionRoot.get(JpaAction_.target).get(JpaTarget_.id), targetId));
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -967,38 +967,29 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
final Long actionId = createTargetAndAssignDs();
|
||||
|
||||
// test and verify
|
||||
controllerManagement
|
||||
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.RUNNING));
|
||||
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING, Action.Status.RUNNING,
|
||||
Action.Status.RUNNING, true);
|
||||
controllerManagement.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.RUNNING));
|
||||
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING, Action.Status.RUNNING, Action.Status.RUNNING, true);
|
||||
|
||||
controllerManagement
|
||||
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.ERROR));
|
||||
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.ERROR, Action.Status.ERROR,
|
||||
Action.Status.ERROR, false);
|
||||
controllerManagement.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.ERROR));
|
||||
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.ERROR, Action.Status.ERROR, Action.Status.ERROR, false);
|
||||
|
||||
// try with disabled late feedback
|
||||
repositoryProperties.setRejectActionStatusForClosedAction(true);
|
||||
controllerManagement
|
||||
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
|
||||
controllerManagement.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
|
||||
|
||||
// test
|
||||
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.ERROR, Action.Status.ERROR,
|
||||
Action.Status.ERROR, false);
|
||||
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.ERROR, Action.Status.ERROR, Action.Status.ERROR, false);
|
||||
|
||||
// try with enabled late feedback - should not make a difference as it
|
||||
// only allows intermediate feedback and not multiple close
|
||||
repositoryProperties.setRejectActionStatusForClosedAction(false);
|
||||
controllerManagement
|
||||
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
|
||||
controllerManagement.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
|
||||
|
||||
// test
|
||||
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.ERROR, Action.Status.ERROR,
|
||||
Action.Status.ERROR, false);
|
||||
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.ERROR, Action.Status.ERROR, Action.Status.ERROR, false);
|
||||
|
||||
assertThat(actionStatusRepository.count()).isEqualTo(3);
|
||||
assertThat(controllerManagement.findActionStatusByAction(PAGE, actionId).getNumberOfElements()).isEqualTo(3);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1716,27 +1707,24 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
actionStatus, true);
|
||||
}
|
||||
|
||||
private void assertActionStatus(final Long actionId, final String controllerId,
|
||||
private void assertActionStatus(
|
||||
final Long actionId, final String controllerId,
|
||||
final TargetUpdateStatus expectedTargetUpdateStatus, final Action.Status expectedActionActionStatus,
|
||||
final Action.Status expectedActionStatus, final boolean actionActive) {
|
||||
final TargetUpdateStatus targetStatus = targetManagement.getByControllerID(controllerId).get()
|
||||
.getUpdateStatus();
|
||||
final TargetUpdateStatus targetStatus = targetManagement.getByControllerID(controllerId).get().getUpdateStatus();
|
||||
assertThat(targetStatus).isEqualTo(expectedTargetUpdateStatus);
|
||||
final Action action = deploymentManagement.findAction(actionId).get();
|
||||
assertThat(action.getStatus()).isEqualTo(expectedActionActionStatus);
|
||||
assertThat(action.isActive()).isEqualTo(actionActive);
|
||||
final List<ActionStatus> actionStatusList = controllerManagement.findActionStatusByAction(PAGE, actionId)
|
||||
.getContent();
|
||||
final List<ActionStatus> actionStatusList = controllerManagement.findActionStatusByAction(PAGE, actionId).getContent();
|
||||
assertThat(actionStatusList.get(actionStatusList.size() - 1).getStatus()).isEqualTo(expectedActionStatus);
|
||||
if (actionActive) {
|
||||
assertThat(controllerManagement.findActiveActionWithHighestWeight(controllerId).get().getId())
|
||||
.isEqualTo(actionId);
|
||||
assertThat(controllerManagement.findActiveActionWithHighestWeight(controllerId).get().getId()).isEqualTo(actionId);
|
||||
}
|
||||
}
|
||||
|
||||
private void createTargetType(String targetTypeName) {
|
||||
systemSecurityContext.runAsSystem(() ->
|
||||
targetTypeManagement.create(entityFactory.targetType().create().name(targetTypeName)));
|
||||
systemSecurityContext.runAsSystem(() -> targetTypeManagement.create(entityFactory.targetType().create().name(targetTypeName)));
|
||||
}
|
||||
|
||||
@Step
|
||||
|
||||
@@ -72,6 +72,12 @@ public class EventVerifier extends AbstractTestExecutionListener {
|
||||
|
||||
@Override
|
||||
public void afterTestMethod(final TestContext testContext) {
|
||||
if (testContext.getTestException() != null) {
|
||||
// test has failed anyway
|
||||
// not expected event set could be result of failed test / incomplete steps - no need to check and mess up with real exception
|
||||
return;
|
||||
}
|
||||
|
||||
final Optional<Expect[]> expectedEvents = getExpectationsFrom(testContext.getTestMethod());
|
||||
try {
|
||||
expectedEvents.ifPresent(this::afterTest);
|
||||
|
||||
Reference in New Issue
Block a user