Feature/ctx aware and access controller2 (#1456)

* Introduce the AccessControlManager and use if for the TargetManagement and TargetTypeManagement.

Signed-off-by: Michael Herdt <Michael.Herdt@bosch.io>

* Extend the access control manager by an API to serialize the current active context and persist it for scheduled background operations like auto-assignment.

Signed-off-by: Michael Herdt <Michael.Herdt@bosch.io>

* Verify modification is permitted before performing automatic assignment

Signed-off-by: Michael Herdt <Michael.Herdt@bosch.io>

* Start with controlling distribution set type access. Perform some refactoring.

Signed-off-by: Michael Herdt <Michael.Herdt@bosch.io>

* Support distribution set access control. Increase character limit to 512 chars for access control context. Refactor default implementations.

Signed-off-by: Michael Herdt <Michael.Herdt@bosch.io>

* Introduce ContextRunner and define admin execution to check for duplicates before creating/updating entities.

Signed-off-by: Michael Herdt <Michael.Herdt@bosch.io>

* Introduce Software Module, Module Type and Artifact control management. Fix tests.

Signed-off-by: Michael Herdt <Michael.Herdt@bosch.io>

* Introduce access controlling test base. Add first test verifying the read operations for target types.

Signed-off-by: Michael Herdt <Michael.Herdt@bosch.io>

* Finalize target type access controlling test.

Signed-off-by: Michael Herdt <Michael.Herdt@bosch.io>

* Introduce ContextRunnerTest and TargetAccessControllingTest.
Signed-off-by: Michael Herdt <Michael.Herdt@bosch.io>

* Introduce DistributionSetAccessControllingTest and fix missing access control specifications.

Signed-off-by: Michael Herdt <Michael.Herdt@bosch.io>

* Extend test cases. Include only updatable targets into rollout.

Signed-off-by: Michael Herdt <Michael.Herdt@bosch.io>

* Fix action visibility.

Signed-off-by: Michael Herdt <Michael.Herdt@bosch.io>

* Modifiable->Updatable & UPDATE check where needed

Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com>

* ContextRunner superseded by ContextAware

+ ContextRunner remaned to ContextAware (move as a cenral entry/concept).
  It now extends (and replace) TenantAware
+ SecurityContextTenantAware becomes ContextAware
+ Pluggable serialization mechanism
  (default Java serialization of contexts) for SecurityContextTenantAware
  (using SecurityContextSerializer)
+ AccessControl methods are added to ensure no entities fill be retrieved
  just to call access control - so, if all permitted - no additional db
  queries will be made
+ &lt;repo type&gt;AccessControl classes removed and replaced with
  AccessControl &lt;repo type&gt; generics
+ AccessControlService removed - every AccessControl is registered and
  overiden independently
+ access_control_context in DB increased to 4k (in order to support java
  security context serialization)
+ needed adaptaion of implemtation and tests done

Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com>

* Refactor SoftModules & DistSets

Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com>

* Refactoring of the Repositories

Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com>

* Repostiotory level permissions

Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com>

* Improvements

Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com>

* Simplification of AccessControl interface

* Simplifications & management package

Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com>

* Implementation improvements

+ Artifact management & repo reviewed and tuned
+ Action(Status) management & repo reviewed and tuned
+ SoftwareModule(Type/Meta) management & repo reviewed and tuned
+ DistributionSet(Type/Tag/Meta) management(+Invalidation) & repo reviewed and tuned
+ Target(Tag/Type/Meta) management & repo reviewed and tuned
+ TargetQueryFilter management & repo reviewed and tuned

* Apply suggestions from code review

Suggestions accepted. Thanks @herdt-michael

Co-authored-by: Michael Herdt <michael.herdt@bosch.com>

* Apply suggestions from code review 2

Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com>

---------

Signed-off-by: Michael Herdt <Michael.Herdt@bosch.io>
Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com>
Co-authored-by: Michael Herdt <Michael.Herdt@bosch.com>
This commit is contained in:
Avgustin Marinov
2023-11-16 11:07:06 +02:00
committed by GitHub
parent 8d487fde33
commit b982039a74
170 changed files with 5371 additions and 3227 deletions

View File

@@ -14,17 +14,20 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.eclipse.hawkbit.ContextAware;
import org.eclipse.hawkbit.autoconfigure.security.MultiUserProperties.User; import org.eclipse.hawkbit.autoconfigure.security.MultiUserProperties.User;
import org.eclipse.hawkbit.im.authentication.PermissionService; import org.eclipse.hawkbit.im.authentication.PermissionService;
import org.eclipse.hawkbit.security.DdiSecurityProperties; import org.eclipse.hawkbit.security.DdiSecurityProperties;
import org.eclipse.hawkbit.security.InMemoryUserAuthoritiesResolver; import org.eclipse.hawkbit.security.InMemoryUserAuthoritiesResolver;
import org.eclipse.hawkbit.security.HawkbitSecurityProperties; import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
import org.eclipse.hawkbit.security.SecurityContextSerializer;
import org.eclipse.hawkbit.security.SecurityContextTenantAware; import org.eclipse.hawkbit.security.SecurityContextTenantAware;
import org.eclipse.hawkbit.security.SecurityTokenGenerator; import org.eclipse.hawkbit.security.SecurityTokenGenerator;
import org.eclipse.hawkbit.security.SpringSecurityAuditorAware; import org.eclipse.hawkbit.security.SpringSecurityAuditorAware;
import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.hawkbit.tenancy.UserAuthoritiesResolver; import org.eclipse.hawkbit.tenancy.UserAuthoritiesResolver;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.security.SecurityProperties; import org.springframework.boot.autoconfigure.security.SecurityProperties;
@@ -49,20 +52,22 @@ import org.springframework.util.CollectionUtils;
public class SecurityAutoConfiguration { public class SecurityAutoConfiguration {
/** /**
* Creates a {@link TenantAware} bean based on the given * Creates a {@link ContextAware} (hence {@link TenantAware}) bean based on the given
* {@link UserAuthoritiesResolver}. * {@link UserAuthoritiesResolver} and {@link SecurityContextSerializer}.
* *
* @param authoritiesResolver * @param authoritiesResolver
* The user authorities/roles resolver * The user authorities/roles resolver
* @param securityContextSerializer
* The security context serializer.
* *
* @return the {@link TenantAware} singleton bean which holds the current * @return the {@link ContextAware} singleton bean.
* {@link TenantAware} service and make it accessible in beans which
* cannot access the service directly, e.g. JPA entities.
*/ */
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public TenantAware tenantAware(final UserAuthoritiesResolver authoritiesResolver) { public ContextAware contextAware(
return new SecurityContextTenantAware(authoritiesResolver); final UserAuthoritiesResolver authoritiesResolver,
@Autowired(required = false) final SecurityContextSerializer securityContextSerializer) {
return new SecurityContextTenantAware(authoritiesResolver, securityContextSerializer);
} }
/** /**

View File

@@ -0,0 +1,62 @@
/**
* Copyright (c) 2023 Bosch.IO GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit;
import org.eclipse.hawkbit.tenancy.TenantAware;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
/**
* {@link ContextAware} provides means for getting the current context (via {@link #getCurrentContext()}) and then
* to execute a {@link Runnable} or a {@link Function} in the same context using {@link #runInContext(String, Runnable)}
* or {@link #runInContext(String, Function, Object)}.
* <p/>
* This is useful for scheduled background operations like rollouts and auto assignments where they shall
* be processed in the scope of the creator.
*/
public interface ContextAware extends TenantAware {
/**
* Return the current context encoded as a {@link String}. Depending on the implementation it could,
* for instance, be a serialized context or a reference to such.
*
* @return could be empty if there is nothing to serialize or context aware is not supported.
*/
Optional<String> getCurrentContext();
/**
* Wrap a specific execution in a known and pre-serialized context.
*
* @param <T> the type of the input to the function
* @param <R> the type of the result of the function
*
* @param serializedContext created by {@link #getCurrentContext()}. Must be non-<code>null</code>.
* @param function function to call in the reconstructed context. Must be non-<code>null</code>.
* @param t the argument that will be passed to the function
* @return the function result
*/
<T, R> R runInContext(String serializedContext, Function<T, R> function, T t);
/**
* Wrap a specific execution in a known and pre-serialized context.
*
* @param serializedContext created by {@link #getCurrentContext()}. Must be non-<code>null</code>.
* @param runnable runnable to call in the reconstructed context. Must be non-<code>null</code>.
*/
default void runInContext(String serializedContext, Runnable runnable) {
Objects.requireNonNull(runnable);
runInContext(serializedContext, v -> {
runnable.run();
return null;
}, null);
}
}

View File

@@ -40,8 +40,6 @@ public interface TenantAware {
* the runner which is implemented to run this specific code * the runner which is implemented to run this specific code
* under the given tenant * under the given tenant
* @return the return type of the {@link TenantRunner} * @return the return type of the {@link TenantRunner}
* @throws any
* kind of {@link RuntimeException}
*/ */
<T> T runAsTenant(String tenant, TenantRunner<T> tenantRunner); <T> T runAsTenant(String tenant, TenantRunner<T> tenantRunner);
@@ -60,8 +58,6 @@ public interface TenantAware {
* the runner which is implemented to run this specific code * the runner which is implemented to run this specific code
* under the given tenant * under the given tenant
* @return the return type of the {@link TenantRunner} * @return the return type of the {@link TenantRunner}
* @throws any
* kind of {@link RuntimeException}
*/ */
<T> T runAsTenantAsUser(String tenant, String username, TenantRunner<T> tenantRunner); <T> T runAsTenantAsUser(String tenant, String username, TenantRunner<T> tenantRunner);

View File

@@ -190,7 +190,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
private List<Target> getTargetsWithoutPendingCancellations(final Set<String> controllerIds) { private List<Target> getTargetsWithoutPendingCancellations(final Set<String> controllerIds) {
return partitionedParallelExecution(controllerIds, partition -> { return partitionedParallelExecution(controllerIds, partition -> {
return targetManagement.getByControllerID(partition).stream().filter(target -> { return targetManagement.getByControllerID(partition).stream().filter(target -> {
if (hasPendingCancellations(target.getControllerId())) { if (hasPendingCancellations(target.getId())) {
LOG.debug("Target {} has pending cancellations. Will not send update message to it.", LOG.debug("Target {} has pending cancellations. Will not send update message to it.",
target.getControllerId()); target.getControllerId());
return false; return false;
@@ -469,8 +469,8 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
return serviceMatcher == null || serviceMatcher.isFromSelf(event); return serviceMatcher == null || serviceMatcher.isFromSelf(event);
} }
private boolean hasPendingCancellations(final String controllerId) { private boolean hasPendingCancellations(final Long targetId) {
return deploymentManagement.hasPendingCancellations(controllerId); return deploymentManagement.hasPendingCancellations(targetId);
} }
protected void sendCancelMessageToTarget(final String tenant, final String controllerId, final Long actionId, protected void sendCancelMessageToTarget(final String tenant, final String controllerId, final Long actionId,

View File

@@ -44,6 +44,7 @@ import org.eclipse.hawkbit.security.DdiSecurityProperties.Authentication.Anonymo
import org.eclipse.hawkbit.security.DdiSecurityProperties.Rp; import org.eclipse.hawkbit.security.DdiSecurityProperties.Rp;
import org.eclipse.hawkbit.security.DmfTenantSecurityToken; import org.eclipse.hawkbit.security.DmfTenantSecurityToken;
import org.eclipse.hawkbit.security.DmfTenantSecurityToken.FileResource; import org.eclipse.hawkbit.security.DmfTenantSecurityToken.FileResource;
import org.eclipse.hawkbit.security.SecurityContextSerializer;
import org.eclipse.hawkbit.security.SecurityContextTenantAware; import org.eclipse.hawkbit.security.SecurityContextTenantAware;
import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.UserAuthoritiesResolver; import org.eclipse.hawkbit.tenancy.UserAuthoritiesResolver;
@@ -113,6 +114,9 @@ public class AmqpControllerAuthenticationTest {
@Mock @Mock
private UserAuthoritiesResolver authoritiesResolver; private UserAuthoritiesResolver authoritiesResolver;
@Mock
private SecurityContextSerializer securityContextSerializer;
@Mock @Mock
private RabbitTemplate rabbitTemplate; private RabbitTemplate rabbitTemplate;
@@ -147,7 +151,7 @@ public class AmqpControllerAuthenticationTest {
when(tenantConfigurationManagementMock.getConfigurationValue(any(), eq(Boolean.class))) when(tenantConfigurationManagementMock.getConfigurationValue(any(), eq(Boolean.class)))
.thenReturn(CONFIG_VALUE_FALSE); .thenReturn(CONFIG_VALUE_FALSE);
final SecurityContextTenantAware tenantAware = new SecurityContextTenantAware(authoritiesResolver); final SecurityContextTenantAware tenantAware = new SecurityContextTenantAware(authoritiesResolver, securityContextSerializer);
final SystemSecurityContext systemSecurityContext = new SystemSecurityContext(tenantAware); final SystemSecurityContext systemSecurityContext = new SystemSecurityContext(tenantAware);
authenticationManager = new AmqpControllerAuthentication(systemManagement, controllerManagement, authenticationManager = new AmqpControllerAuthentication(systemManagement, controllerManagement,

View File

@@ -149,9 +149,10 @@ class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTest {
assertThat(softwareModule.getArtifacts().isEmpty()).as("Artifact list for softwaremodule should be empty") assertThat(softwareModule.getArtifacts().isEmpty()).as("Artifact list for softwaremodule should be empty")
.isTrue(); .isTrue();
assertThat(softwareModule.getMetadata()).containsExactly( assertThat(softwareModule.getMetadata()).allSatisfy(metadata -> {
new DmfMetadata(TestdataFactory.VISIBLE_SM_MD_KEY, TestdataFactory.VISIBLE_SM_MD_VALUE)); assertThat(metadata.getKey()).isEqualTo(TestdataFactory.VISIBLE_SM_MD_KEY);
assertThat(metadata.getValue()).isEqualTo(TestdataFactory.VISIBLE_SM_MD_VALUE);
});
for (final SoftwareModule softwareModule2 : action.getDistributionSet().getModules()) { for (final SoftwareModule softwareModule2 : action.getDistributionSet().getModules()) {
if (!softwareModule.getModuleId().equals(softwareModule2.getId())) { if (!softwareModule.getModuleId().equals(softwareModule2.getId())) {
continue; continue;

View File

@@ -63,6 +63,7 @@ import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TenantConfigurationValue; import org.eclipse.hawkbit.repository.model.TenantConfigurationValue;
import org.eclipse.hawkbit.security.DmfTenantSecurityToken; import org.eclipse.hawkbit.security.DmfTenantSecurityToken;
import org.eclipse.hawkbit.security.DmfTenantSecurityToken.FileResource; import org.eclipse.hawkbit.security.DmfTenantSecurityToken.FileResource;
import org.eclipse.hawkbit.security.SecurityContextSerializer;
import org.eclipse.hawkbit.security.SecurityContextTenantAware; import org.eclipse.hawkbit.security.SecurityContextTenantAware;
import org.eclipse.hawkbit.security.SecurityTokenGenerator; import org.eclipse.hawkbit.security.SecurityTokenGenerator;
import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.security.SystemSecurityContext;
@@ -148,6 +149,9 @@ public class AmqpMessageHandlerServiceTest {
@Mock @Mock
private UserAuthoritiesResolver authoritiesResolver; private UserAuthoritiesResolver authoritiesResolver;
@Mock
private SecurityContextSerializer securityContextSerializer;
@Captor @Captor
private ArgumentCaptor<Map<String, String>> attributesCaptor; private ArgumentCaptor<Map<String, String>> attributesCaptor;
@@ -179,7 +183,7 @@ public class AmqpMessageHandlerServiceTest {
lenient().when(tenantConfigurationManagement.getConfigurationValue(MULTI_ASSIGNMENTS_ENABLED, Boolean.class)) lenient().when(tenantConfigurationManagement.getConfigurationValue(MULTI_ASSIGNMENTS_ENABLED, Boolean.class))
.thenReturn(multiAssignmentConfig); .thenReturn(multiAssignmentConfig);
final SecurityContextTenantAware tenantAware = new SecurityContextTenantAware(authoritiesResolver); final SecurityContextTenantAware tenantAware = new SecurityContextTenantAware(authoritiesResolver, securityContextSerializer);
final SystemSecurityContext systemSecurityContext = new SystemSecurityContext(tenantAware); final SystemSecurityContext systemSecurityContext = new SystemSecurityContext(tenantAware);
amqpMessageHandlerService = new AmqpMessageHandlerService(rabbitTemplate, amqpMessageDispatcherServiceMock, amqpMessageHandlerService = new AmqpMessageHandlerService(rabbitTemplate, amqpMessageDispatcherServiceMock,

View File

@@ -489,7 +489,7 @@ class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServiceIntegr
@Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6),
@Expect(type = DistributionSetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 2) }) @Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 2) })
void receiveDownLoadAndInstallMessageAfterAssignment() { void receiveDownloadAndInstallMessageAfterAssignment() {
final String controllerId = TARGET_PREFIX + "receiveDownLoadAndInstallMessageAfterAssignment"; final String controllerId = TARGET_PREFIX + "receiveDownLoadAndInstallMessageAfterAssignment";
// setup // setup

View File

@@ -68,22 +68,6 @@ public interface ArtifactManagement {
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
Artifact create(@NotNull @Valid ArtifactUpload artifactUpload); Artifact create(@NotNull @Valid ArtifactUpload artifactUpload);
/**
* Garbage collects artifact binaries if only referenced by given
* {@link SoftwareModule#getId()} or {@link SoftwareModule}'s that are
* marked as deleted.
*
*
* @param artifactSha1Hash
* no longer needed
* @param moduleId
* the garbage collection call is made for
*
* @return <code>true</code> if an binary was actually garbage collected
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
boolean clearArtifactBinary(@NotEmpty String artifactSha1Hash, long moduleId);
/** /**
* Deletes {@link Artifact} based on given id. * Deletes {@link Artifact} based on given id.
* *
@@ -151,7 +135,7 @@ public interface ArtifactManagement {
* *
* @param pageReq * @param pageReq
* Pageable parameter * Pageable parameter
* @param swId * @param softwareModuleId
* software module id * software module id
* @return Page<Artifact> * @return Page<Artifact>
* *
@@ -159,12 +143,12 @@ public interface ArtifactManagement {
* if software module with given ID does not exist * if software module with given ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
Page<Artifact> findBySoftwareModule(@NotNull Pageable pageReq, long swId); Page<Artifact> findBySoftwareModule(@NotNull Pageable pageReq, long softwareModuleId);
/** /**
* Count local artifacts for a base software module. * Count local artifacts for a base software module.
* *
* @param swId * @param softwareModuleId
* software module id * software module id
* @return count by software module * @return count by software module
* *
@@ -172,7 +156,7 @@ public interface ArtifactManagement {
* if software module with given ID does not exist * if software module with given ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
long countBySoftwareModule(long swId); long countBySoftwareModule(long softwareModuleId);
/** /**
* Loads {@link DbArtifact} from store for given {@link Artifact}. * Loads {@link DbArtifact} from store for given {@link Artifact}.

View File

@@ -472,17 +472,6 @@ public interface ControllerManagement {
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
void updateActionExternalRef(long actionId, @NotEmpty String externalRef); void updateActionExternalRef(long actionId, @NotEmpty String externalRef);
/**
* Retrieves list of {@link Action}s which matches the provided
* externalRefs.
*
* @param externalRefs
* for which the actions need to be fetched.
* @return list of {@link Action}s matching the externalRefs.
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
List<Action> getActiveActionsByExternalRef(@NotNull List<String> externalRefs);
/** /**
* Retrieves an {@link Action} using {@link Action#getExternalRef()} * Retrieves an {@link Action} using {@link Action#getExternalRef()}
* *

View File

@@ -207,12 +207,10 @@ public interface DeploymentManagement {
long countActionsByTarget(@NotNull String rsqlParam, @NotEmpty String controllerId); long countActionsByTarget(@NotNull String rsqlParam, @NotEmpty String controllerId);
/** /**
* @return the total amount of stored action status * Returns total count of all actions
*/ * <p/>
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) * No access control applied.
long countActionStatusAll(); *
/**
* @return the total amount of stored actions * @return the total amount of stored actions
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
@@ -220,6 +218,8 @@ public interface DeploymentManagement {
/** /**
* Counts the actions which match the given query. * Counts the actions which match the given query.
* <p/>
* No access control applied.
* *
* @param rsqlParam * @param rsqlParam
* RSQL query. * RSQL query.
@@ -241,29 +241,6 @@ public interface DeploymentManagement {
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
long countActionsByTarget(@NotEmpty String controllerId); long countActionsByTarget(@NotEmpty String controllerId);
/**
* Counts all active {@link Action}s referring to the given DistributionSet.
*
* @param distributionSet
* DistributionSet to count the {@link Action}s from
* @return the count of actions referring to the given distributionSet
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
long countActionsByDistributionSetIdAndActiveIsTrue(Long distributionSet);
/**
* Counts all active {@link Action}s referring to the given DistributionSet
* that are not in a given state.
*
* @param distributionSet
* DistributionSet to count the {@link Action}s from
* @param status
* the state the actions should not have
* @return the count of actions referring to the given distributionSet
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
long countActionsByDistributionSetIdAndActiveIsTrueAndStatusIsNot(Long distributionSet, Status status);
/** /**
* Get the {@link Action} entity for given actionId. * Get the {@link Action} entity for given actionId.
* *
@@ -277,9 +254,10 @@ public interface DeploymentManagement {
/** /**
* Retrieves all {@link Action}s from repository. * Retrieves all {@link Action}s from repository.
* <p/>
* No access control applied.
* *
* @param pageable * @param pageable pagination parameter
* pagination parameter
* @return a paged list of {@link Action}s * @return a paged list of {@link Action}s
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
@@ -287,35 +265,17 @@ public interface DeploymentManagement {
/** /**
* Retrieves all {@link Action} entities which match the given RSQL query. * Retrieves all {@link Action} entities which match the given RSQL query.
* <p/>
* No access control applied.
* *
* @param rsqlParam * @param rsqlParam RSQL query string
* RSQL query string * @param pageable the page request parameter for paging and sorting the result
* @param pageable
* the page request parameter for paging and sorting the result
* *
* @return a paged list of {@link Action}s. * @return a paged list of {@link Action}s.
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Slice<Action> findActions(@NotNull String rsqlParam, @NotNull Pageable pageable); Slice<Action> findActions(@NotNull String rsqlParam, @NotNull Pageable pageable);
/**
* Retrieves all {@link Action} which assigned to a specific
* {@link DistributionSet}.
*
* @param pageable
* the page request parameter for paging and sorting the result
* @param distributionSetId
* the distribution set which should be assigned to the actions
* in the result
* @return a list of {@link Action} which are assigned to a specific
* {@link DistributionSet}
*
* @throws EntityNotFoundException
* if distribution set with given ID does not exist
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Slice<Action> findActionsByDistributionSet(@NotNull Pageable pageable, long distributionSetId);
/** /**
* Retrieves all {@link Action}s assigned to a specific {@link Target} and a * Retrieves all {@link Action}s assigned to a specific {@link Target} and a
* given specification. * given specification.
@@ -384,7 +344,8 @@ public interface DeploymentManagement {
/** /**
* Retrieves all messages for an {@link ActionStatus}. * Retrieves all messages for an {@link ActionStatus}.
* * <p/>
* No entity based access control applied.
* *
* @param pageable * @param pageable
* the page request parameter for paging and sorting the result * the page request parameter for paging and sorting the result
@@ -397,14 +358,15 @@ public interface DeploymentManagement {
/** /**
* Counts all messages for an {@link ActionStatus}. * Counts all messages for an {@link ActionStatus}.
* <p/>
* No access control applied.
* *
* * @deprecated Used by UI only. With future removal of UI it could be removed.
* @param pageable
* the page request parameter for paging and sorting the result
* @param actionStatusId * @param actionStatusId
* the id of {@link ActionStatus} to count the messages from * the id of {@link ActionStatus} to count the messages from
* @return count of messages by a specific {@link ActionStatus} id * @return count of messages by a specific {@link ActionStatus} id
*/ */
@Deprecated(forRemoval = true)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
long countMessagesByActionStatusId(long actionStatusId); long countMessagesByActionStatusId(long actionStatusId);
@@ -520,6 +482,8 @@ public interface DeploymentManagement {
/** /**
* Starts all scheduled actions of an RolloutGroup parent. * Starts all scheduled actions of an RolloutGroup parent.
* <p/>
* No entity based access control applied.
* *
* @param rolloutId * @param rolloutId
* the rollout the actions belong to * the rollout the actions belong to
@@ -533,16 +497,6 @@ public interface DeploymentManagement {
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
long startScheduledActionsByRolloutGroupParent(long rolloutId, long distributionSetId, Long rolloutGroupParentId); long startScheduledActionsByRolloutGroupParent(long rolloutId, long distributionSetId, Long rolloutGroupParentId);
/**
* All {@link ActionStatus} entries in the repository.
*
* @param pageable
* the pagination parameter
* @return {@link Page} of {@link ActionStatus} entries
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Page<ActionStatus> findActionStatusAll(@NotNull Pageable pageable);
/** /**
* Returns {@link DistributionSet} that is assigned to given {@link Target}. * Returns {@link DistributionSet} that is assigned to given {@link Target}.
* *
@@ -570,7 +524,9 @@ public interface DeploymentManagement {
/** /**
* Deletes actions which match one of the given action status and which have * Deletes actions which match one of the given action status and which have
* not been modified since the given (absolute) time-stamp. * not been modified since the given (absolute) time-stamp. Used for obsolete actions cleanup.
* <p/>
* No entity based access control applied.
* *
* @param status * @param status
* Set of action status. * Set of action status.
@@ -586,12 +542,11 @@ public interface DeploymentManagement {
* Checks if there is an action for the device with the given controller ID * Checks if there is an action for the device with the given controller ID
* that is in the {@link Action.Status#CANCELING} state. * that is in the {@link Action.Status#CANCELING} state.
* *
* @param controllerId * @param targetId of target
* of target
* @return if actions in CANCELING state are present * @return if actions in CANCELING state are present
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
boolean hasPendingCancellations(@NotEmpty String controllerId); boolean hasPendingCancellations(@NotNull Long targetId);
/** /**
* Cancels all actions that refer to a given distribution set. This method * Cancels all actions that refer to a given distribution set. This method

View File

@@ -53,7 +53,7 @@ public interface DistributionSetManagement
/** /**
* Assigns {@link SoftwareModule} to existing {@link DistributionSet}. * Assigns {@link SoftwareModule} to existing {@link DistributionSet}.
* *
* @param setId * @param id
* to assign and update * to assign and update
* @param moduleIds * @param moduleIds
* to get assigned * to get assigned
@@ -76,13 +76,13 @@ public interface DistributionSetManagement
* for the addressed {@link DistributionSet}. * for the addressed {@link DistributionSet}.
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
DistributionSet assignSoftwareModules(long setId, @NotEmpty Collection<Long> moduleIds); DistributionSet assignSoftwareModules(long id, @NotEmpty Collection<Long> moduleIds);
/** /**
* Assign a {@link DistributionSetTag} assignment to given * Assign a {@link DistributionSetTag} assignment to given
* {@link DistributionSet}s. * {@link DistributionSet}s.
* *
* @param setIds * @param ids
* to assign for * to assign for
* @param tagId * @param tagId
* to assign * to assign
@@ -94,12 +94,12 @@ public interface DistributionSetManagement
* distribution sets. * distribution sets.
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
List<DistributionSet> assignTag(@NotEmpty Collection<Long> setIds, long tagId); List<DistributionSet> assignTag(@NotEmpty Collection<Long> ids, long tagId);
/** /**
* Creates a list of distribution set meta data entries. * Creates a list of distribution set meta data entries.
* *
* @param setId * @param id
* if the {@link DistributionSet} the metadata has to be created * if the {@link DistributionSet} the metadata has to be created
* for * for
* @param metadata * @param metadata
@@ -118,12 +118,12 @@ public interface DistributionSetManagement
* for the addressed {@link DistributionSet} * for the addressed {@link DistributionSet}
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
List<DistributionSetMetadata> createMetaData(long setId, @NotEmpty Collection<MetaData> metadata); List<DistributionSetMetadata> createMetaData(long id, @NotEmpty Collection<MetaData> metadata);
/** /**
* Deletes a distribution set meta data entry. * Deletes a distribution set meta data entry.
* *
* @param setId * @param id
* where meta data has to be deleted * where meta data has to be deleted
* @param key * @param key
* of the meta data element * of the meta data element
@@ -132,7 +132,7 @@ public interface DistributionSetManagement
* if given set does not exist * if given set does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
void deleteMetaData(long setId, @NotEmpty String key); void deleteMetaData(long id, @NotEmpty String key);
/** /**
* Retrieves the distribution set for a given action. * Retrieves the distribution set for a given action.
@@ -154,13 +154,13 @@ public interface DistributionSetManagement
* Note: for performance reasons it is recommended to use {@link #get(Long)} * Note: for performance reasons it is recommended to use {@link #get(Long)}
* if details are not necessary. * if details are not necessary.
* *
* @param setId * @param id
* to look for. * to look for.
* *
* @return {@link DistributionSet} * @return {@link DistributionSet}
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
Optional<DistributionSet> getWithDetails(long setId); Optional<DistributionSet> getWithDetails(long id);
/** /**
* Find distribution set by name and version. * Find distribution set by name and version.
@@ -234,7 +234,7 @@ public interface DistributionSetManagement
* *
* @param pageable * @param pageable
* the page request to page the result * the page request to page the result
* @param setId * @param id
* the distribution set id to retrieve the meta data from * the distribution set id to retrieve the meta data from
* *
* @return a paged result of all meta data entries for a given distribution * @return a paged result of all meta data entries for a given distribution
@@ -244,14 +244,12 @@ public interface DistributionSetManagement
* if distribution set with given ID does not exist * if distribution set with given ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
Page<DistributionSetMetadata> findMetaDataByDistributionSetId(@NotNull Pageable pageable, long setId); Page<DistributionSetMetadata> findMetaDataByDistributionSetId(@NotNull Pageable pageable, long id);
/** /**
* Counts all meta data by the given distribution set id. * Counts all meta data by the given distribution set id.
* *
* @param pageable * @param id
* the page request to page the result
* @param setId
* the distribution set id to retrieve the meta data count from * the distribution set id to retrieve the meta data count from
* *
* @return count of ds metadata * @return count of ds metadata
@@ -260,14 +258,14 @@ public interface DistributionSetManagement
* if distribution set with given ID does not exist * if distribution set with given ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
long countMetaDataByDistributionSetId(long setId); long countMetaDataByDistributionSetId(long id);
/** /**
* Finds all meta data by the given distribution set id. * Finds all meta data by the given distribution set id.
* *
* @param pageable * @param pageable
* the page request to page the result * the page request to page the result
* @param setId * @param id
* the distribution set id to retrieve the meta data from * the distribution set id to retrieve the meta data from
* @param rsqlParam * @param rsqlParam
* rsql query string * rsql query string
@@ -286,8 +284,8 @@ public interface DistributionSetManagement
* of distribution set with given ID does not exist * of distribution set with given ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
Page<DistributionSetMetadata> findMetaDataByDistributionSetIdAndRsql(@NotNull Pageable pageable, long setId, Page<DistributionSetMetadata> findMetaDataByDistributionSetIdAndRsql(@NotNull Pageable pageable,
@NotNull String rsqlParam); long id, @NotNull String rsqlParam);
/** /**
* Finds all {@link DistributionSet}s based on completeness. * Finds all {@link DistributionSet}s based on completeness.
@@ -409,7 +407,7 @@ public interface DistributionSetManagement
/** /**
* Finds a single distribution set meta data by its id. * Finds a single distribution set meta data by its id.
* *
* @param setId * @param id
* of the {@link DistributionSet} * of the {@link DistributionSet}
* @param key * @param key
* of the meta data element * of the meta data element
@@ -419,19 +417,19 @@ public interface DistributionSetManagement
* is set with given ID does not exist * is set with given ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
Optional<DistributionSetMetadata> getMetaDataByDistributionSetId(long setId, @NotEmpty String key); Optional<DistributionSetMetadata> getMetaDataByDistributionSetId(long id, @NotEmpty String key);
/** /**
* Checks if a {@link DistributionSet} is currently in use by a target in * Checks if a {@link DistributionSet} is currently in use by a target in
* the repository. * the repository.
* *
* @param setId * @param id
* to check * to check
* *
* @return <code>true</code> if in use * @return <code>true</code> if in use
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
boolean isInUse(long setId); boolean isInUse(long id);
/** /**
* Toggles {@link DistributionSetTag} assignment to given * Toggles {@link DistributionSetTag} assignment to given
@@ -439,7 +437,7 @@ public interface DistributionSetManagement
* the list have the {@link Tag} not yet assigned, they will be. Only if all * the list have the {@link Tag} not yet assigned, they will be. Only if all
* of theme have the tag already assigned they will be removed instead. * of theme have the tag already assigned they will be removed instead.
* *
* @param setIds * @param ids
* to toggle for * to toggle for
* @param tagName * @param tagName
* to toggle * to toggle
@@ -450,13 +448,13 @@ public interface DistributionSetManagement
* if given tag does not exist or (at least one) module * if given tag does not exist or (at least one) module
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
DistributionSetTagAssignmentResult toggleTagAssignment(@NotEmpty Collection<Long> setIds, @NotNull String tagName); DistributionSetTagAssignmentResult toggleTagAssignment(@NotEmpty Collection<Long> ids, @NotNull String tagName);
/** /**
* Unassigns a {@link SoftwareModule} form an existing * Unassigns a {@link SoftwareModule} form an existing
* {@link DistributionSet}. * {@link DistributionSet}.
* *
* @param setId * @param id
* to get unassigned form * to get unassigned form
* @param moduleId * @param moduleId
* to be unassigned * to be unassigned
@@ -470,13 +468,13 @@ public interface DistributionSetManagement
* the DS is already in use. * the DS is already in use.
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
DistributionSet unassignSoftwareModule(long setId, long moduleId); DistributionSet unassignSoftwareModule(long id, long moduleId);
/** /**
* Unassign a {@link DistributionSetTag} assignment to given * Unassign a {@link DistributionSetTag} assignment to given
* {@link DistributionSet}. * {@link DistributionSet}.
* *
* @param setId * @param id
* to unassign for * to unassign for
* @param tagId * @param tagId
* to unassign * to unassign
@@ -486,12 +484,12 @@ public interface DistributionSetManagement
* if set or tag with given ID does not exist * if set or tag with given ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
DistributionSet unAssignTag(long setId, long tagId); DistributionSet unAssignTag(long id, long tagId);
/** /**
* Updates a distribution set meta data value if corresponding entry exists. * Updates a distribution set meta data value if corresponding entry exists.
* *
* @param setId * @param id
* {@link DistributionSet} of the meta data entry to be updated * {@link DistributionSet} of the meta data entry to be updated
* @param metadata * @param metadata
* meta data entry to be updated * meta data entry to be updated
@@ -502,7 +500,7 @@ public interface DistributionSetManagement
* updated * updated
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
DistributionSetMetadata updateMetaData(long setId, @NotNull MetaData metadata); DistributionSetMetadata updateMetaData(long id, @NotNull MetaData metadata);
/** /**
* Count all {@link DistributionSet}s in the repository that are not marked * Count all {@link DistributionSet}s in the repository that are not marked
@@ -523,48 +521,47 @@ public interface DistributionSetManagement
* Count all {@link org.eclipse.hawkbit.repository.model.Rollout}s by status for * Count all {@link org.eclipse.hawkbit.repository.model.Rollout}s by status for
* Distribution Set. * Distribution Set.
* *
* @param dsId * @param id
* to look for * to look for
* *
* @return List of Statistics for {@link org.eclipse.hawkbit.repository.model.Rollout}s status counts * @return List of Statistics for {@link org.eclipse.hawkbit.repository.model.Rollout}s status counts
* *
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
List<Statistic> countRolloutsByStatusForDistributionSet(@NotNull Long dsId); List<Statistic> countRolloutsByStatusForDistributionSet(@NotNull Long id);
/** /**
* Count all {@link org.eclipse.hawkbit.repository.model.Action}s by status for * Count all {@link org.eclipse.hawkbit.repository.model.Action}s by status for
* Distribution Set. * Distribution Set.
* *
* @param dsId * @param id
* to look for * to look for
* *
* @return List of Statistics for {@link org.eclipse.hawkbit.repository.model.Action}s status counts * @return List of Statistics for {@link org.eclipse.hawkbit.repository.model.Action}s status counts
* *
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
List<Statistic> countActionsByStatusForDistributionSet(@NotNull Long dsId); List<Statistic> countActionsByStatusForDistributionSet(@NotNull Long id);
/** /**
* Count all {@link org.eclipse.hawkbit.repository.builder.AutoAssignDistributionSetUpdate}s * Count all {@link org.eclipse.hawkbit.repository.builder.AutoAssignDistributionSetUpdate}s
* for Distribution Set. * for Distribution Set.
* *
* @param dsId * @param id
* to look for * to look for
* *
* @return number of {@link org.eclipse.hawkbit.repository.builder.AutoAssignDistributionSetUpdate}s * @return number of {@link org.eclipse.hawkbit.repository.builder.AutoAssignDistributionSetUpdate}s
* *
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
Long countAutoAssignmentsForDistributionSet(@NotNull Long dsId); Long countAutoAssignmentsForDistributionSet(@NotNull Long id);
/** /**
* Sets the specified {@link DistributionSet} as invalidated. * Sets the specified {@link DistributionSet} as invalidated.
* *
* @param set * @param distributionSet
* the ID of the {@link DistributionSet} to be set to invalid * the ID of the {@link DistributionSet} to be set to invalid
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
void invalidate(DistributionSet set); void invalidate(DistributionSet distributionSet);
} }

View File

@@ -61,7 +61,7 @@ public interface DistributionSetTagManagement extends RepositoryManagement<Distr
* @param pageable * @param pageable
* information for page size, offset and sort order. * information for page size, offset and sort order.
* *
* @param setId * @param distributionSetId
* of the {@link DistributionSet} * of the {@link DistributionSet}
* @return page of the found {@link TargetTag}s * @return page of the found {@link TargetTag}s
* *
@@ -69,6 +69,5 @@ public interface DistributionSetTagManagement extends RepositoryManagement<Distr
* if {@link DistributionSet} with given ID does not exist * if {@link DistributionSet} with given ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
Page<DistributionSetTag> findByDistributionSet(@NotNull Pageable pageable, long setId); Page<DistributionSetTag> findByDistributionSet(@NotNull Pageable pageable, long distributionSetId);
} }

View File

@@ -52,7 +52,7 @@ public interface DistributionSetTypeManagement
/** /**
* Assigns {@link DistributionSetType#getMandatoryModuleTypes()}. * Assigns {@link DistributionSetType#getMandatoryModuleTypes()}.
* *
* @param dsTypeId * @param id
* to update * to update
* @param softwareModuleTypeIds * @param softwareModuleTypeIds
* to assign * to assign
@@ -71,15 +71,14 @@ public interface DistributionSetTypeManagement
* exceeded for the addressed {@link DistributionSetType} * exceeded for the addressed {@link DistributionSetType}
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
DistributionSetType assignOptionalSoftwareModuleTypes(long dsTypeId, DistributionSetType assignOptionalSoftwareModuleTypes(long id, @NotEmpty Collection<Long> softwareModuleTypeIds);
@NotEmpty Collection<Long> softwareModuleTypeIds);
/** /**
* Assigns {@link DistributionSetType#getOptionalModuleTypes()}. * Assigns {@link DistributionSetType#getOptionalModuleTypes()}.
* *
* @param dsTypeId * @param id
* to update * to update
* @param softwareModuleTypes * @param softwareModuleTypeIds
* to assign * to assign
* @return updated {@link DistributionSetType} * @return updated {@link DistributionSetType}
* *
@@ -96,17 +95,16 @@ public interface DistributionSetTypeManagement
* exceeded for the addressed {@link DistributionSetType} * exceeded for the addressed {@link DistributionSetType}
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
DistributionSetType assignMandatorySoftwareModuleTypes(long dsTypeId, DistributionSetType assignMandatorySoftwareModuleTypes(long id, @NotEmpty Collection<Long> softwareModuleTypeIds);
@NotEmpty Collection<Long> softwareModuleTypes);
/** /**
* Unassigns a {@link SoftwareModuleType} from the * Unassigns a {@link SoftwareModuleType} from the
* {@link DistributionSetType}. Does nothing if {@link SoftwareModuleType} * {@link DistributionSetType}. Does nothing if {@link SoftwareModuleType}
* has not been assigned in the first place. * has not been assigned in the first place.
* *
* @param dsTypeId * @param id
* to update * to update
* @param softwareModuleId * @param softwareModuleTypeId
* to unassign * to unassign
* @return updated {@link DistributionSetType} * @return updated {@link DistributionSetType}
* *
@@ -118,6 +116,5 @@ public interface DistributionSetTypeManagement
* by a {@link DistributionSet} * by a {@link DistributionSet}
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
DistributionSetType unassignSoftwareModuleType(long dsTypeId, long softwareModuleId); DistributionSetType unassignSoftwareModuleType(long id, long softwareModuleTypeId);
} }

View File

@@ -68,6 +68,8 @@ public interface RolloutManagement {
/** /**
* Counts all {@link Rollout}s for a specific {@link DistributionSet} that * Counts all {@link Rollout}s for a specific {@link DistributionSet} that
* are stoppable * are stoppable
* <p/>
* No access control applied
* *
* @param setId * @param setId
* the distribution set * the distribution set

View File

@@ -108,7 +108,7 @@ public interface SoftwareModuleManagement
/** /**
* Deletes a software module meta data entry. * Deletes a software module meta data entry.
* *
* @param moduleId * @param id
* where meta data has to be deleted * where meta data has to be deleted
* @param key * @param key
* of the metda data element * of the metda data element
@@ -117,14 +117,14 @@ public interface SoftwareModuleManagement
* of module or metadata entry does not exist * of module or metadata entry does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
void deleteMetaData(long moduleId, @NotEmpty String key); void deleteMetaData(long id, @NotEmpty String key);
/** /**
* Returns all modules assigned to given {@link DistributionSet}. * Returns all modules assigned to given {@link DistributionSet}.
* *
* @param pageable * @param pageable
* the page request to page the result set * the page request to page the result set
* @param setId * @param distributionSetId
* to search for * to search for
* *
* @return all {@link SoftwareModule}s that are assigned to given * @return all {@link SoftwareModule}s that are assigned to given
@@ -134,12 +134,12 @@ public interface SoftwareModuleManagement
* if distribution set with given ID does not exist * if distribution set with given ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
Page<SoftwareModule> findByAssignedTo(@NotNull Pageable pageable, long setId); Page<SoftwareModule> findByAssignedTo(@NotNull Pageable pageable, long distributionSetId);
/** /**
* Returns count of all modules assigned to given {@link DistributionSet}. * Returns count of all modules assigned to given {@link DistributionSet}.
* *
* @param setId * @param distributionSetId
* to search for * to search for
* *
* @return count of {@link SoftwareModule}s that are assigned to given * @return count of {@link SoftwareModule}s that are assigned to given
@@ -149,7 +149,7 @@ public interface SoftwareModuleManagement
* if distribution set with given ID does not exist * if distribution set with given ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
long countByAssignedTo(long setId); long countByAssignedTo(long distributionSetId);
/** /**
* Filter {@link SoftwareModule}s with given * Filter {@link SoftwareModule}s with given
@@ -192,7 +192,7 @@ public interface SoftwareModuleManagement
/** /**
* Finds a single software module meta data by its id. * Finds a single software module meta data by its id.
* *
* @param moduleId * @param id
* where meta data has to be found * where meta data has to be found
* @param key * @param key
* of the meta data element * of the meta data element
@@ -202,14 +202,14 @@ public interface SoftwareModuleManagement
* is module with given ID does not exist * is module with given ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
Optional<SoftwareModuleMetadata> getMetaDataBySoftwareModuleId(long moduleId, @NotEmpty String key); Optional<SoftwareModuleMetadata> getMetaDataBySoftwareModuleId(long id, @NotEmpty String key);
/** /**
* Finds all meta data by the given software module id. * Finds all meta data by the given software module id.
* *
* @param pageable * @param pageable
* the page request to page the result * the page request to page the result
* @param moduleId * @param id
* the software module id to retrieve the meta data from * the software module id to retrieve the meta data from
* *
* @return a paged result of all meta data entries for a given software * @return a paged result of all meta data entries for a given software
@@ -219,12 +219,12 @@ public interface SoftwareModuleManagement
* if software module with given ID does not exist * if software module with given ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
Page<SoftwareModuleMetadata> findMetaDataBySoftwareModuleId(@NotNull Pageable pageable, long moduleId); Page<SoftwareModuleMetadata> findMetaDataBySoftwareModuleId(@NotNull Pageable pageable, long id);
/** /**
* Counts all meta data by the given software module id. * Counts all meta data by the given software module id.
* *
* @param moduleId * @param id
* the software module id to retrieve the meta data count from * the software module id to retrieve the meta data count from
* *
* @return count of all meta data entries for a given software module id * @return count of all meta data entries for a given software module id
@@ -233,7 +233,7 @@ public interface SoftwareModuleManagement
* if software module with given ID does not exist * if software module with given ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
long countMetaDataBySoftwareModuleId(long moduleId); long countMetaDataBySoftwareModuleId(long id);
/** /**
* Finds all meta data by the given software module id where * Finds all meta data by the given software module id where
@@ -241,7 +241,7 @@ public interface SoftwareModuleManagement
* *
* @param pageable * @param pageable
* the page request to page the result * the page request to page the result
* @param moduleId * @param id
* the software module id to retrieve the meta data from * the software module id to retrieve the meta data from
* *
* @return a paged result of all meta data entries for a given software * @return a paged result of all meta data entries for a given software
@@ -252,14 +252,14 @@ public interface SoftwareModuleManagement
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
Page<SoftwareModuleMetadata> findMetaDataBySoftwareModuleIdAndTargetVisible(@NotNull Pageable pageable, Page<SoftwareModuleMetadata> findMetaDataBySoftwareModuleIdAndTargetVisible(@NotNull Pageable pageable,
long moduleId); long id);
/** /**
* Finds all meta data by the given software module id. * Finds all meta data by the given software module id.
* *
* @param pageable * @param pageable
* the page request to page the result * the page request to page the result
* @param moduleId * @param id
* the software module id to retrieve the meta data from * the software module id to retrieve the meta data from
* @param rsqlParam * @param rsqlParam
* filter definition in RSQL syntax * filter definition in RSQL syntax
@@ -278,7 +278,7 @@ public interface SoftwareModuleManagement
* if software module with given ID does not exist * if software module with given ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
Page<SoftwareModuleMetadata> findMetaDataByRsql(@NotNull Pageable pageable, long moduleId, Page<SoftwareModuleMetadata> findMetaDataByRsql(@NotNull Pageable pageable, long id,
@NotNull String rsqlParam); @NotNull String rsqlParam);
/** /**
@@ -296,7 +296,7 @@ public interface SoftwareModuleManagement
* *
* @param pageable * @param pageable
* page parameter * page parameter
* @param orderByDistributionId * @param orderByDistributionSetId
* the ID of distribution set to be ordered on top * the ID of distribution set to be ordered on top
* @param searchText * @param searchText
* filtered as "like" on {@link SoftwareModule#getName()} * filtered as "like" on {@link SoftwareModule#getName()}
@@ -310,7 +310,7 @@ public interface SoftwareModuleManagement
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
Slice<AssignedSoftwareModule> findAllOrderBySetAssignmentAndModuleNameAscModuleVersionAsc( Slice<AssignedSoftwareModule> findAllOrderBySetAssignmentAndModuleNameAscModuleVersionAsc(
@NotNull Pageable pageable, long orderByDistributionId, String searchText, Long typeId); @NotNull Pageable pageable, long orderByDistributionSetId, String searchText, Long typeId);
/** /**
* Retrieves the {@link SoftwareModule}s by their {@link SoftwareModuleType} * Retrieves the {@link SoftwareModule}s by their {@link SoftwareModuleType}

View File

@@ -111,6 +111,8 @@ public interface TargetFilterQueryManagement {
/** /**
* Counts all target filters that have a given auto assign distribution set * Counts all target filters that have a given auto assign distribution set
* assigned. * assigned.
* <p/>
* No access control applied
* *
* @param autoAssignDistributionSetId * @param autoAssignDistributionSetId
* the id of the distribution set * the id of the distribution set

View File

@@ -69,13 +69,13 @@ public interface TargetManagement {
/** /**
* Counts number of targets with the given distribution set assigned. * Counts number of targets with the given distribution set assigned.
* *
* @param distId to search for * @param distributionSetId to search for
* @return number of found {@link Target}s. * @return number of found {@link Target}s.
* @throws EntityNotFoundException if distribution set with given ID does not exist * @throws EntityNotFoundException if distribution set with given ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET + SpringEvalExpressions.HAS_AUTH_OR @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) + SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
long countByAssignedDistributionSet(long distId); long countByAssignedDistributionSet(long distributionSetId);
/** /**
* Count {@link Target}s for all the given filter parameters. * Count {@link Target}s for all the given filter parameters.
@@ -95,25 +95,25 @@ public interface TargetManagement {
/** /**
* Get the count of targets with the given distribution set id. * Get the count of targets with the given distribution set id.
* *
* @param distId to search for * @param distributionSetId to search for
* @return number of found {@link Target}s. * @return number of found {@link Target}s.
* @throws EntityNotFoundException if distribution set with given ID does not exist * @throws EntityNotFoundException if distribution set with given ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET + SpringEvalExpressions.HAS_AUTH_OR @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) + SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
long countByInstalledDistributionSet(long distId); long countByInstalledDistributionSet(long distributionSetId);
/** /**
* Checks if there is already a {@link Target} that has the given * Checks if there is already a {@link Target} that has the given
* distribution set Id assigned or installed. * distribution set Id assigned or installed.
* *
* @param distId to search for * @param distributionSetId to search for
* @return <code>true</code> if a {@link Target} exists. * @return <code>true</code> if a {@link Target} exists.
* @throws EntityNotFoundException if distribution set with given ID does not exist * @throws EntityNotFoundException if distribution set with given ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET + SpringEvalExpressions.HAS_AUTH_OR @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) + SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
boolean existsByInstalledOrAssignedDistributionSet(long distId); boolean existsByInstalledOrAssignedDistributionSet(long distributionSetId);
/** /**
* Count {@link TargetFilterQuery}s for given target filter query. * Count {@link TargetFilterQuery}s for given target filter query.
@@ -131,13 +131,13 @@ public interface TargetManagement {
* *
* @param rsqlParam * @param rsqlParam
* filter definition in RSQL syntax * filter definition in RSQL syntax
* @param dsTypeId * @param distributionSetId
* ID of the {@link DistributionSetType} the targets need to be * ID of the {@link DistributionSetType} the targets need to be
* compatible with * compatible with
* @return the found number of{@link Target}s * @return the found number of{@link Target}s
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
long countByRsqlAndCompatible(@NotEmpty String rsqlParam, @NotNull Long dsTypeId); long countByRsqlAndCompatible(@NotEmpty String rsqlParam, @NotNull Long distributionSetId);
/** /**
* Count all targets with failed actions for specific Rollout * Count all targets with failed actions for specific Rollout
@@ -214,31 +214,31 @@ public interface TargetManagement {
/** /**
* Deletes all targets with the given IDs. * Deletes all targets with the given IDs.
* *
* @param targetIDs * @param ids
* the IDs of the targets to be deleted * the IDs of the targets to be deleted
* *
* @throws EntityNotFoundException * @throws EntityNotFoundException
* if (at least one) of the given target IDs does not exist * if (at least one) of the given target IDs does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_TARGET)
void delete(@NotEmpty Collection<Long> targetIDs); void delete(@NotEmpty Collection<Long> ids);
/** /**
* Deletes target with the given controller ID. * Deletes target with the given controller ID.
* *
* @param controllerID * @param controllerId
* the controller ID of the target to be deleted * the controller ID of the target to be deleted
* *
* @throws EntityNotFoundException * @throws EntityNotFoundException
* if target with given ID does not exist * if target with given ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_TARGET)
void deleteByControllerID(@NotEmpty String controllerID); void deleteByControllerID(@NotEmpty String controllerId);
/** /**
* Finds all targets for all the given parameter {@link TargetFilterQuery} * Finds all targets for all the given parameter {@link TargetFilterQuery} and
* and that don't have the specified distribution set in their action * that don't have the specified distribution set in their action history and
* history and are compatible with the passed {@link DistributionSetType}. * are compatible with the passed {@link DistributionSetType}.
* *
* @param pageRequest * @param pageRequest
* the pageRequest to enhance the query for paging and sorting * the pageRequest to enhance the query for paging and sorting
@@ -251,9 +251,9 @@ public interface TargetManagement {
* @throws EntityNotFoundException * @throws EntityNotFoundException
* if distribution set with given ID does not exist * if distribution set with given ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
Slice<Target> findByTargetFilterQueryAndNonDSAndCompatible(@NotNull Pageable pageRequest, long distributionSetId, Slice<Target> findByTargetFilterQueryAndNonDSAndCompatibleAndUpdatable(@NotNull Pageable pageRequest,
@NotNull String rsqlParam); long distributionSetId, @NotNull String rsqlParam);
/** /**
* Counts all targets for all the given parameter {@link TargetFilterQuery} * Counts all targets for all the given parameter {@link TargetFilterQuery}
@@ -269,8 +269,8 @@ public interface TargetManagement {
* @throws EntityNotFoundException * @throws EntityNotFoundException
* if distribution set with given ID does not exist * if distribution set with given ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
long countByRsqlAndNonDSAndCompatible(long distributionSetId, @NotNull String rsqlParam); long countByRsqlAndNonDSAndCompatibleAndUpdatable(long distributionSetId, @NotNull String rsqlParam);
/** /**
* Finds all targets for all the given parameter {@link TargetFilterQuery} * Finds all targets for all the given parameter {@link TargetFilterQuery}
@@ -285,11 +285,11 @@ public interface TargetManagement {
* filter definition in RSQL syntax * filter definition in RSQL syntax
* @param distributionSetType * @param distributionSetType
* type of the {@link DistributionSet} the targets must be * type of the {@link DistributionSet} the targets must be
* compatible with * compatible withs
* @return a page of the found {@link Target}s * @return a page of the found {@link Target}s
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
Slice<Target> findByTargetFilterQueryAndNotInRolloutGroupsAndCompatible(@NotNull Pageable pageRequest, Slice<Target> findByTargetFilterQueryAndNotInRolloutGroupsAndCompatibleAndUpdatable(@NotNull Pageable pageRequest,
@NotEmpty Collection<Long> groups, @NotNull String rsqlParam, @NotEmpty Collection<Long> groups, @NotNull String rsqlParam,
@NotNull DistributionSetType distributionSetType); @NotNull DistributionSetType distributionSetType);
@@ -320,13 +320,13 @@ public interface TargetManagement {
* @param rsqlParam * @param rsqlParam
* filter definition in RSQL syntax * filter definition in RSQL syntax
* @param distributionSetType * @param distributionSetType
* type of the {@link DistributionSet} the targets must be * type of the {@link DistributionSet} the targets must be compatible
* compatible with * with
* @return count of the found {@link Target}s * @return count of the found {@link Target}s
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
long countByRsqlAndNotInRolloutGroupsAndCompatible(@NotEmpty Collection<Long> groups, @NotNull String rsqlParam, long countByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable(@NotEmpty Collection<Long> groups,
@NotNull DistributionSetType distributionSetType); @NotNull String rsqlParam, @NotNull DistributionSetType distributionSetType);
/** /**
* Counts all targets with failed actions for specific Rollout * Counts all targets with failed actions for specific Rollout
@@ -363,7 +363,7 @@ public interface TargetManagement {
* *
* @param pageReq * @param pageReq
* page parameter * page parameter
* @param distributionSetID * @param distributionSetId
* the ID of the {@link DistributionSet} * the ID of the {@link DistributionSet}
* *
* *
@@ -373,7 +373,7 @@ public interface TargetManagement {
* if distribution set with given ID does not exist * if distribution set with given ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_READ_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_READ_TARGET)
Page<Target> findByAssignedDistributionSet(@NotNull Pageable pageReq, long distributionSetID); Page<Target> findByAssignedDistributionSet(@NotNull Pageable pageReq, long distributionSetId);
/** /**
* Retrieves {@link Target}s by the assigned {@link DistributionSet} * Retrieves {@link Target}s by the assigned {@link DistributionSet}
@@ -381,7 +381,7 @@ public interface TargetManagement {
* *
* @param pageReq * @param pageReq
* page parameter * page parameter
* @param distributionSetID * @param distributionSetId
* the ID of the {@link DistributionSet} * the ID of the {@link DistributionSet}
* @param rsqlParam * @param rsqlParam
* the specification to filter the result set * the specification to filter the result set
@@ -396,7 +396,7 @@ public interface TargetManagement {
* if distribution set with given ID does not exist * if distribution set with given ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_READ_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_READ_TARGET)
Page<Target> findByAssignedDistributionSetAndRsql(@NotNull Pageable pageReq, long distributionSetID, Page<Target> findByAssignedDistributionSetAndRsql(@NotNull Pageable pageReq, long distributionSetId,
@NotNull String rsqlParam); @NotNull String rsqlParam);
/** /**
@@ -442,7 +442,7 @@ public interface TargetManagement {
* *
* @param pageReq * @param pageReq
* page parameter * page parameter
* @param distributionSetID * @param distributionSetId
* the ID of the {@link DistributionSet} * the ID of the {@link DistributionSet}
* *
* @return the found {@link Target}s * @return the found {@link Target}s
@@ -451,7 +451,7 @@ public interface TargetManagement {
* if distribution set with given ID does not exist * if distribution set with given ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_READ_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_READ_TARGET)
Page<Target> findByInstalledDistributionSet(@NotNull Pageable pageReq, long distributionSetID); Page<Target> findByInstalledDistributionSet(@NotNull Pageable pageReq, long distributionSetId);
/** /**
* retrieves {@link Target}s by the installed {@link DistributionSet} * retrieves {@link Target}s by the installed {@link DistributionSet}
@@ -557,7 +557,7 @@ public interface TargetManagement {
* *
* @param pageable * @param pageable
* the page request to page the result set * the page request to page the result set
* @param orderByDistributionId * @param orderByDistributionSetId
* {@link DistributionSet#getId()} to be ordered by * {@link DistributionSet#getId()} to be ordered by
* @param filterParams * @param filterParams
* the filters to apply; only filters are enabled that have non-null * the filters to apply; only filters are enabled that have non-null
@@ -569,7 +569,7 @@ public interface TargetManagement {
* if distribution set with given ID does not exist * if distribution set with given ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Slice<Target> findByFilterOrderByLinkedDistributionSet(@NotNull Pageable pageable, long orderByDistributionId, Slice<Target> findByFilterOrderByLinkedDistributionSet(@NotNull Pageable pageable, long orderByDistributionSetId,
@NotNull FilterParams filterParams); @NotNull FilterParams filterParams);
/** /**
@@ -656,12 +656,12 @@ public interface TargetManagement {
* assignment outcome. * assignment outcome.
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
TargetTypeAssignmentResult unAssignType(@NotEmpty Collection<String> controllerIds); TargetTypeAssignmentResult unassignType(@NotEmpty Collection<String> controllerIds);
/** /**
* Un-assign a {@link TargetTag} assignment to given {@link Target}. * Un-assign a {@link TargetTag} assignment to given {@link Target}.
* *
* @param controllerID * @param controllerId
* to un-assign for * to un-assign for
* @param targetTagId * @param targetTagId
* to un-assign * to un-assign
@@ -671,23 +671,23 @@ public interface TargetManagement {
* if TAG with given ID does not exist * if TAG with given ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
Target unAssignTag(@NotEmpty String controllerID, long targetTagId); Target unassignTag(@NotEmpty String controllerId, long targetTagId);
/** /**
* Un-assign a {@link TargetType} assignment to given {@link Target}. * Un-assign a {@link TargetType} assignment to given {@link Target}.
* *
* @param controllerID * @param controllerId
* to un-assign for * to un-assign for
* @return the unassigned target * @return the unassigned target
* *
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
Target unAssignType(@NotEmpty String controllerID); Target unassignType(@NotEmpty String controllerId);
/** /**
* Assign a {@link TargetType} assignment to given {@link Target}. * Assign a {@link TargetType} assignment to given {@link Target}.
* *
* @param controllerID * @param controllerId
* to un-assign for * to un-assign for
* @param targetTypeId * @param targetTypeId
* Target type id * Target type id
@@ -697,7 +697,7 @@ public interface TargetManagement {
* if TargetType with given target ID does not exist * if TargetType with given target ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
Target assignType(@NotEmpty String controllerID, @NotNull Long targetTypeId); Target assignType(@NotEmpty String controllerId, @NotNull Long targetTypeId);
/** /**
* updates the {@link Target}. * updates the {@link Target}.
@@ -940,5 +940,4 @@ public interface TargetManagement {
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
TargetMetadata updateMetadata(@NotEmpty String controllerId, @NotNull MetaData metadata); TargetMetadata updateMetadata(@NotEmpty String controllerId, @NotNull MetaData metadata);
} }

View File

@@ -164,24 +164,23 @@ public interface TargetTypeManagement {
TargetType update(@NotNull @Valid TargetTypeUpdate update); TargetType update(@NotNull @Valid TargetTypeUpdate update);
/** /**
* @param targetTypeId * @param id
* Target type ID * Target type ID
* @param distributionSetTypeIds * @param distributionSetTypeIds
* Distribution set ID * Distribution set ID
* @return Target type * @return Target type
*/ */
@PreAuthorize(SpPermission.SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET) @PreAuthorize(SpPermission.SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
TargetType assignCompatibleDistributionSetTypes(long targetTypeId, TargetType assignCompatibleDistributionSetTypes(long id,
@NotEmpty Collection<Long> distributionSetTypeIds); @NotEmpty Collection<Long> distributionSetTypeIds);
/** /**
* @param targetTypeId * @param id
* Target type ID * Target type ID
* @param distributionSetTypeIds * @param distributionSetTypeIds
* Distribution set ID * Distribution set ID
* @return Target type * @return Target type
*/ */
@PreAuthorize(SpPermission.SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET) @PreAuthorize(SpPermission.SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
TargetType unassignDistributionSetType(long targetTypeId, long distributionSetTypeIds); TargetType unassignDistributionSetType(long id, long distributionSetTypeIds);
} }

View File

@@ -36,7 +36,11 @@ public class InsufficientPermissionException extends AbstractServerRtException {
/** /**
* creates new InsufficientPermissionException. * creates new InsufficientPermissionException.
*/ */
public InsufficientPermissionException(final String message) {
super(message, SpServerError.SP_INSUFFICIENT_PERMISSION);
}
public InsufficientPermissionException() { public InsufficientPermissionException() {
this(null); super(SpServerError.SP_INSUFFICIENT_PERMISSION, null);
} }
} }

View File

@@ -108,6 +108,11 @@ public interface Rollout extends NamedEntity {
*/ */
Optional<Integer> getWeight(); Optional<Integer> getWeight();
/**
* @return the stored access control context (if present)
*/
Optional<String> getAccessControlContext();
/** /**
* *
* State machine for rollout. * State machine for rollout.

View File

@@ -73,8 +73,7 @@ public interface TargetFilterQuery extends TenantAwareBaseEntity {
ActionType getAutoAssignActionType(); ActionType getAutoAssignActionType();
/** /**
* @return the weight of the {@link Action}s created during an auto * @return the weight of the {@link Action}s created during an auto assignment.
* assignment.
*/ */
Optional<Integer> getAutoAssignWeight(); Optional<Integer> getAutoAssignWeight();
@@ -88,4 +87,10 @@ public interface TargetFilterQuery extends TenantAwareBaseEntity {
* (considered with confirmation flow active) * (considered with confirmation flow active)
*/ */
boolean isConfirmationRequired(); boolean isConfirmationRequired();
/**
* Defining a serialized access control context which needs to be set when
* performing the auto-assignment by the scheduler
*/
Optional<String> getAccessControlContext();
} }

View File

@@ -1,60 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa;
import java.io.Serializable;
import java.util.List;
import java.util.Optional;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaTenantAwareBaseEntity;
import org.eclipse.hawkbit.repository.model.BaseEntity;
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.transaction.annotation.Transactional;
/**
* Command repository operations for all {@link TenantAwareBaseEntity}s.
*
* @param <T>
* type if the entity type
* @param <I>
* of the entity type
*/
@NoRepositoryBean
@Transactional(readOnly = true)
public interface BaseEntityRepository<T extends AbstractJpaTenantAwareBaseEntity, I extends Serializable>
extends PagingAndSortingRepository<T, I>, CrudRepository<T, I>, NoCountSliceRepository<T> {
/**
* Retrieves an {@link BaseEntity} by its id.
*
* @param id
* to search for
* @return {@link BaseEntity}
*/
Optional<T> findById(I id);
/**
* Overrides
* {@link org.springframework.data.repository.CrudRepository#saveAll(Iterable)}
* to return a list of created entities instead of an instance of
* {@link Iterable} to be able to work with it directly in further code
* processing instead of converting the {@link Iterable}.
*
* @param entities
* to persist in the database
* @return the created entities
*/
@Override
@Transactional
<S extends T> List<S> saveAll(Iterable<S> entities);
}

View File

@@ -9,6 +9,7 @@
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa;
import org.eclipse.hawkbit.repository.jpa.management.JpaSystemManagement;
import org.springframework.cache.interceptor.KeyGenerator; import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;

View File

@@ -11,10 +11,12 @@ package org.eclipse.hawkbit.repository.jpa;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Optional;
import javax.persistence.EntityManager; import javax.persistence.EntityManager;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity; import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity;
import org.eclipse.hawkbit.repository.jpa.repository.NoCountSliceRepository;
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder; import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageImpl;
@@ -33,6 +35,11 @@ public final class JpaManagementHelper {
private JpaManagementHelper() { private JpaManagementHelper() {
} }
public static <T, J extends T> Optional<J> findOneBySpec(final JpaSpecificationExecutor<J> repository,
final List<Specification<J>> specList) {
return repository.findOne(combineWithAnd(specList));
}
public static <T, J extends T> Page<T> findAllWithCountBySpec(final JpaSpecificationExecutor<J> repository, public static <T, J extends T> Page<T> findAllWithCountBySpec(final JpaSpecificationExecutor<J> repository,
final Pageable pageable, final List<Specification<J>> specList) { final Pageable pageable, final List<Specification<J>> specList) {
if (CollectionUtils.isEmpty(specList)) { if (CollectionUtils.isEmpty(specList)) {
@@ -46,7 +53,7 @@ public final class JpaManagementHelper {
return new PageImpl<>(Collections.unmodifiableList(jpaAll.getContent()), pageable, jpaAll.getTotalElements()); return new PageImpl<>(Collections.unmodifiableList(jpaAll.getContent()), pageable, jpaAll.getTotalElements());
} }
private static <J> Specification<J> combineWithAnd(final List<Specification<J>> specList) { public static <J> Specification<J> combineWithAnd(final List<Specification<J>> specList) {
return specList.size() == 1 ? specList.get(0) : SpecificationsBuilder.combineWithAnd(specList); return specList.size() == 1 ? specList.get(0) : SpecificationsBuilder.combineWithAnd(specList);
} }

View File

@@ -35,6 +35,10 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout; import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup; import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup;
import org.eclipse.hawkbit.repository.jpa.model.RolloutTargetGroup; import org.eclipse.hawkbit.repository.jpa.model.RolloutTargetGroup;
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
import org.eclipse.hawkbit.repository.jpa.repository.RolloutGroupRepository;
import org.eclipse.hawkbit.repository.jpa.repository.RolloutRepository;
import org.eclipse.hawkbit.repository.jpa.repository.RolloutTargetGroupRepository;
import org.eclipse.hawkbit.repository.jpa.rollout.condition.EvaluatorNotConfiguredException; import org.eclipse.hawkbit.repository.jpa.rollout.condition.EvaluatorNotConfiguredException;
import org.eclipse.hawkbit.repository.jpa.rollout.condition.RolloutGroupEvaluationManager; import org.eclipse.hawkbit.repository.jpa.rollout.condition.RolloutGroupEvaluationManager;
import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper; import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper;
@@ -529,8 +533,8 @@ public class JpaRolloutExecutor implements RolloutExecutor {
if (!RolloutHelper.isRolloutRetried(rollout.getTargetFilterQuery())) { if (!RolloutHelper.isRolloutRetried(rollout.getTargetFilterQuery())) {
targetsInGroupFilter = DeploymentHelper.runInNewTransaction(txManager, targetsInGroupFilter = DeploymentHelper.runInNewTransaction(txManager,
"countAllTargetsByTargetFilterQueryAndNotInRolloutGroups", "countAllTargetsByTargetFilterQueryAndNotInRolloutGroups",
count -> targetManagement.countByRsqlAndNotInRolloutGroupsAndCompatible(readyGroups, groupTargetFilter, count -> targetManagement.countByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable(readyGroups,
rollout.getDistributionSet().getType())); groupTargetFilter, rollout.getDistributionSet().getType()));
} else { } else {
targetsInGroupFilter = DeploymentHelper.runInNewTransaction(txManager, targetsInGroupFilter = DeploymentHelper.runInNewTransaction(txManager,
"countByFailedRolloutAndNotInRolloutGroupsAndCompatible", "countByFailedRolloutAndNotInRolloutGroupsAndCompatible",
@@ -582,7 +586,7 @@ public class JpaRolloutExecutor implements RolloutExecutor {
RolloutGroupStatus.READY, group); RolloutGroupStatus.READY, group);
Slice<Target> targets; Slice<Target> targets;
if (!RolloutHelper.isRolloutRetried(rollout.getTargetFilterQuery())) { if (!RolloutHelper.isRolloutRetried(rollout.getTargetFilterQuery())) {
targets = targetManagement.findByTargetFilterQueryAndNotInRolloutGroupsAndCompatible( targets = targetManagement.findByTargetFilterQueryAndNotInRolloutGroupsAndCompatibleAndUpdatable(
pageRequest, readyGroups, targetFilter, rollout.getDistributionSet().getType()); pageRequest, readyGroups, targetFilter, rollout.getDistributionSet().getType());
} else { } else {
targets = targetManagement.findByFailedRolloutAndNotInRolloutGroups( targets = targetManagement.findByFailedRolloutAndNotInRolloutGroups(

View File

@@ -10,14 +10,13 @@
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Lock;
import org.eclipse.hawkbit.ContextAware;
import org.eclipse.hawkbit.repository.RolloutExecutor; import org.eclipse.hawkbit.repository.RolloutExecutor;
import org.eclipse.hawkbit.repository.RolloutHandler; import org.eclipse.hawkbit.repository.RolloutHandler;
import org.eclipse.hawkbit.repository.RolloutManagement; import org.eclipse.hawkbit.repository.RolloutManagement;
import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper; import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper;
import org.eclipse.hawkbit.repository.model.BaseEntity;
import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.tenancy.TenantAware;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -35,6 +34,7 @@ public class JpaRolloutHandler implements RolloutHandler {
private final RolloutExecutor rolloutExecutor; private final RolloutExecutor rolloutExecutor;
private final LockRegistry lockRegistry; private final LockRegistry lockRegistry;
private final PlatformTransactionManager txManager; private final PlatformTransactionManager txManager;
private final ContextAware contextAware;
/** /**
* Constructor * Constructor
@@ -52,12 +52,14 @@ public class JpaRolloutHandler implements RolloutHandler {
*/ */
public JpaRolloutHandler(final TenantAware tenantAware, final RolloutManagement rolloutManagement, public JpaRolloutHandler(final TenantAware tenantAware, final RolloutManagement rolloutManagement,
final RolloutExecutor rolloutExecutor, final LockRegistry lockRegistry, final RolloutExecutor rolloutExecutor, final LockRegistry lockRegistry,
final PlatformTransactionManager txManager) { final PlatformTransactionManager txManager,
final ContextAware contextAware) {
this.tenantAware = tenantAware; this.tenantAware = tenantAware;
this.rolloutManagement = rolloutManagement; this.rolloutManagement = rolloutManagement;
this.rolloutExecutor = rolloutExecutor; this.rolloutExecutor = rolloutExecutor;
this.lockRegistry = lockRegistry; this.lockRegistry = lockRegistry;
this.txManager = txManager; this.txManager = txManager;
this.contextAware = contextAware;
} }
@Override @Override
@@ -92,19 +94,29 @@ public class JpaRolloutHandler implements RolloutHandler {
return tenant + "-rollout"; return tenant + "-rollout";
} }
// run in a tenant context, i.e. contextAware.getCurrentTenant() returns the tenant
// the rollout is made for
private void handleRolloutInNewTransaction(final long rolloutId, final String handlerId) { private void handleRolloutInNewTransaction(final long rolloutId, final String handlerId) {
DeploymentHelper.runInNewTransaction(txManager, handlerId + "-" + rolloutId, status -> { DeploymentHelper.runInNewTransaction(txManager, handlerId + "-" + rolloutId, status -> {
rolloutManagement.get(rolloutId).ifPresentOrElse( rolloutManagement.get(rolloutId).ifPresentOrElse(
rollout -> runInUserContext(rollout, () -> rolloutExecutor.execute(rollout)), rollout -> {
rollout.getAccessControlContext().ifPresentOrElse(
context -> // has stored context - executes it with it
contextAware.runInContext(
context,
() -> rolloutExecutor.execute(rollout)),
() -> // has no stored context - executes it in the tenant & user scope
contextAware.runAsTenantAsUser(
contextAware.getCurrentTenant(),
rollout.getCreatedBy(), () -> {
rolloutExecutor.execute(rollout);
return null;
})
);
},
() -> LOGGER.error("Could not retrieve rollout with id {}. Will not continue with execution.", () -> LOGGER.error("Could not retrieve rollout with id {}. Will not continue with execution.",
rolloutId)); rolloutId));
return 0L; return 0L;
}); });
} }
private void runInUserContext(final BaseEntity rollout, final Runnable handler) {
DeploymentHelper.runInNonSystemContext(handler, () -> Objects.requireNonNull(rollout.getCreatedBy()),
tenantAware);
}
} }

View File

@@ -1,25 +0,0 @@
/**
* Copyright (c) 2021 Bosch.IO GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa;
import org.eclipse.hawkbit.repository.BaseRepositoryTypeProvider;
/**
* Simple implementation of {@link BaseRepositoryTypeProvider} leveraging our
* {@link SimpleJpaWithNoCountRepository} for all current use cases
*/
public class NoCountBaseRepositoryTypeProvider implements BaseRepositoryTypeProvider {
@Override
public Class<?> getBaseRepositoryType(final Class<?> repositoryType) {
return SimpleJpaWithNoCountRepository.class;
}
}

View File

@@ -17,6 +17,7 @@ import javax.persistence.EntityManager;
import javax.sql.DataSource; import javax.sql.DataSource;
import javax.validation.Validation; import javax.validation.Validation;
import org.eclipse.hawkbit.ContextAware;
import org.eclipse.hawkbit.artifact.repository.ArtifactRepository; import org.eclipse.hawkbit.artifact.repository.ArtifactRepository;
import org.eclipse.hawkbit.repository.ArtifactEncryption; import org.eclipse.hawkbit.repository.ArtifactEncryption;
import org.eclipse.hawkbit.repository.ArtifactEncryptionSecretsStore; import org.eclipse.hawkbit.repository.ArtifactEncryptionSecretsStore;
@@ -62,6 +63,7 @@ import org.eclipse.hawkbit.repository.event.ApplicationEventFilter;
import org.eclipse.hawkbit.repository.event.remote.EventEntityManager; import org.eclipse.hawkbit.repository.event.remote.EventEntityManager;
import org.eclipse.hawkbit.repository.event.remote.EventEntityManagerHolder; import org.eclipse.hawkbit.repository.event.remote.EventEntityManagerHolder;
import org.eclipse.hawkbit.repository.event.remote.TargetPollEvent; import org.eclipse.hawkbit.repository.event.remote.TargetPollEvent;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.eclipse.hawkbit.repository.jpa.aspects.ExceptionMappingAspectHandler; import org.eclipse.hawkbit.repository.jpa.aspects.ExceptionMappingAspectHandler;
import org.eclipse.hawkbit.repository.jpa.autoassign.AutoAssignChecker; import org.eclipse.hawkbit.repository.jpa.autoassign.AutoAssignChecker;
import org.eclipse.hawkbit.repository.jpa.autoassign.AutoAssignScheduler; import org.eclipse.hawkbit.repository.jpa.autoassign.AutoAssignScheduler;
@@ -80,10 +82,55 @@ import org.eclipse.hawkbit.repository.jpa.configuration.MultiTenantJpaTransactio
import org.eclipse.hawkbit.repository.jpa.event.JpaEventEntityManager; import org.eclipse.hawkbit.repository.jpa.event.JpaEventEntityManager;
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitDefaultServiceExecutor; import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitDefaultServiceExecutor;
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor; import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
import org.eclipse.hawkbit.repository.jpa.management.JpaArtifactManagement;
import org.eclipse.hawkbit.repository.jpa.management.JpaConfirmationManagement;
import org.eclipse.hawkbit.repository.jpa.management.JpaControllerManagement;
import org.eclipse.hawkbit.repository.jpa.management.JpaDeploymentManagement;
import org.eclipse.hawkbit.repository.jpa.management.JpaDistributionSetInvalidationManagement;
import org.eclipse.hawkbit.repository.jpa.management.JpaDistributionSetManagement;
import org.eclipse.hawkbit.repository.jpa.management.JpaDistributionSetTagManagement;
import org.eclipse.hawkbit.repository.jpa.management.JpaDistributionSetTypeManagement;
import org.eclipse.hawkbit.repository.jpa.management.JpaRolloutGroupManagement;
import org.eclipse.hawkbit.repository.jpa.management.JpaRolloutManagement;
import org.eclipse.hawkbit.repository.jpa.management.JpaSoftwareModuleManagement;
import org.eclipse.hawkbit.repository.jpa.management.JpaSoftwareModuleTypeManagement;
import org.eclipse.hawkbit.repository.jpa.management.JpaSystemManagement;
import org.eclipse.hawkbit.repository.jpa.management.JpaTargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.jpa.management.JpaTargetManagement;
import org.eclipse.hawkbit.repository.jpa.management.JpaTargetTagManagement;
import org.eclipse.hawkbit.repository.jpa.management.JpaTargetTypeManagement;
import org.eclipse.hawkbit.repository.jpa.management.JpaTenantConfigurationManagement;
import org.eclipse.hawkbit.repository.jpa.management.JpaTenantStatsManagement;
import org.eclipse.hawkbit.repository.jpa.model.JpaArtifact;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType;
import org.eclipse.hawkbit.repository.jpa.model.helper.AfterTransactionCommitExecutorHolder; import org.eclipse.hawkbit.repository.jpa.model.helper.AfterTransactionCommitExecutorHolder;
import org.eclipse.hawkbit.repository.jpa.model.helper.EntityInterceptorHolder; import org.eclipse.hawkbit.repository.jpa.model.helper.EntityInterceptorHolder;
import org.eclipse.hawkbit.repository.jpa.model.helper.SecurityTokenGeneratorHolder; import org.eclipse.hawkbit.repository.jpa.model.helper.SecurityTokenGeneratorHolder;
import org.eclipse.hawkbit.repository.jpa.model.helper.TenantAwareHolder; import org.eclipse.hawkbit.repository.jpa.model.helper.TenantAwareHolder;
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
import org.eclipse.hawkbit.repository.jpa.repository.ActionStatusRepository;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetMetadataRepository;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetRepository;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetTagRepository;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetTypeRepository;
import org.eclipse.hawkbit.repository.jpa.repository.LocalArtifactRepository;
import org.eclipse.hawkbit.repository.jpa.repository.RolloutGroupRepository;
import org.eclipse.hawkbit.repository.jpa.repository.RolloutRepository;
import org.eclipse.hawkbit.repository.jpa.repository.RolloutTargetGroupRepository;
import org.eclipse.hawkbit.repository.jpa.repository.HawkBitBaseRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleMetadataRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleTypeRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetFilterQueryRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetMetadataRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetTagRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetTypeRepository;
import org.eclipse.hawkbit.repository.jpa.rollout.RolloutScheduler; import org.eclipse.hawkbit.repository.jpa.rollout.RolloutScheduler;
import org.eclipse.hawkbit.repository.jpa.rollout.condition.PauseRolloutGroupAction; import org.eclipse.hawkbit.repository.jpa.rollout.condition.PauseRolloutGroupAction;
import org.eclipse.hawkbit.repository.jpa.rollout.condition.RolloutGroupActionEvaluator; import org.eclipse.hawkbit.repository.jpa.rollout.condition.RolloutGroupActionEvaluator;
@@ -116,7 +163,10 @@ import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.persistence.config.PersistenceUnitProperties; import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.hibernate.validator.BaseHibernateValidatorConfiguration; import org.hibernate.validator.BaseHibernateValidatorConfiguration;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -135,6 +185,7 @@ import org.springframework.data.domain.AuditorAware;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing; import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.integration.support.locks.LockRegistry; import org.springframework.integration.support.locks.LockRegistry;
import org.springframework.lang.NonNull;
import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter; import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter;
import org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect; import org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect;
import org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter; import org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter;
@@ -152,7 +203,7 @@ import com.google.common.collect.Maps;
* General configuration for hawkBit's Repository. * General configuration for hawkBit's Repository.
* *
*/ */
@EnableJpaRepositories(value = "org.eclipse.hawkbit.repository.jpa", repositoryFactoryBeanClass = CustomBaseRepositoryFactoryBean.class) @EnableJpaRepositories(value = "org.eclipse.hawkbit.repository.jpa.repository", repositoryFactoryBeanClass = CustomBaseRepositoryFactoryBean.class)
@EnableTransactionManagement @EnableTransactionManagement
@EnableJpaAuditing @EnableJpaAuditing
@EnableAspectJAutoProxy @EnableAspectJAutoProxy
@@ -266,8 +317,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/** /**
* @param dsTypeManagement * @param dsTypeManagement
* for loading * for loading {@link TargetType#getCompatibleDistributionSetTypes()}
* {@link TargetType#getCompatibleDistributionSetTypes()}
* @return TargetTypeBuilder bean * @return TargetTypeBuilder bean
*/ */
@Bean @Bean
@@ -282,10 +332,9 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
} }
/** /**
* @param softwareManagement * @param softwareModuleTypeManagement
* for loading * for loading {@link DistributionSetType#getMandatoryModuleTypes()}
* {@link DistributionSetType#getMandatoryModuleTypes()} and * and {@link DistributionSetType#getOptionalModuleTypes()}
* {@link DistributionSetType#getOptionalModuleTypes()}
* @return DistributionSetTypeBuilder bean * @return DistributionSetTypeBuilder bean
*/ */
@Bean @Bean
@@ -327,8 +376,8 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/** /**
* @return the {@link SystemSecurityContext} singleton bean which make it * @return the {@link SystemSecurityContext} singleton bean which make it
* accessible in beans which cannot access the service directly, * accessible in beans which cannot access the service directly, e.g.
* e.g. JPA entities. * JPA entities.
*/ */
@Bean @Bean
SystemSecurityContextHolder systemSecurityContextHolder() { SystemSecurityContextHolder systemSecurityContextHolder() {
@@ -336,9 +385,9 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
} }
/** /**
* @return the {@link TenantConfigurationManagement} singleton bean which * @return the {@link TenantConfigurationManagement} singleton bean which make
* make it accessible in beans which cannot access the service * it accessible in beans which cannot access the service directly, e.g.
* directly, e.g. JPA entities. * JPA entities.
*/ */
@Bean @Bean
TenantConfigurationManagementHolder tenantConfigurationManagementHolder() { TenantConfigurationManagementHolder tenantConfigurationManagementHolder() {
@@ -347,9 +396,8 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/** /**
* @return the {@link SystemManagementHolder} singleton bean which holds the * @return the {@link SystemManagementHolder} singleton bean which holds the
* current {@link SystemManagement} service and make it accessible * current {@link SystemManagement} service and make it accessible in
* in beans which cannot access the service directly, e.g. JPA * beans which cannot access the service directly, e.g. JPA entities.
* entities.
*/ */
@Bean @Bean
SystemManagementHolder systemManagementHolder() { SystemManagementHolder systemManagementHolder() {
@@ -357,10 +405,9 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
} }
/** /**
* @return the {@link TenantAwareHolder} singleton bean which holds the * @return the {@link TenantAwareHolder} singleton bean which holds the current
* current {@link TenantAware} service and make it accessible in * {@link TenantAware} service and make it accessible in beans which
* beans which cannot access the service directly, e.g. JPA * cannot access the service directly, e.g. JPA entities.
* entities.
*/ */
@Bean @Bean
TenantAwareHolder tenantAwareHolder() { TenantAwareHolder tenantAwareHolder() {
@@ -368,10 +415,9 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
} }
/** /**
* @return the {@link SecurityTokenGeneratorHolder} singleton bean which * @return the {@link SecurityTokenGeneratorHolder} singleton bean which holds
* holds the current {@link SecurityTokenGenerator} service and make * the current {@link SecurityTokenGenerator} service and make it
* it accessible in beans which cannot access the service via * accessible in beans which cannot access the service via injection
* injection
*/ */
@Bean @Bean
SecurityTokenGeneratorHolder securityTokenGeneratorHolder() { SecurityTokenGeneratorHolder securityTokenGeneratorHolder() {
@@ -404,6 +450,8 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
@Bean @Bean
public MethodValidationPostProcessor methodValidationPostProcessor() { public MethodValidationPostProcessor methodValidationPostProcessor() {
final MethodValidationPostProcessor processor = new MethodValidationPostProcessor(); final MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
// ValidatorFactory shall NOT be closed because after closing the generated Validator
// methods shall not be called - we need the validator in future
processor.setValidator(Validation.byDefaultProvider().configure() processor.setValidator(Validation.byDefaultProvider().configure()
.addProperty(BaseHibernateValidatorConfiguration.ALLOW_PARALLEL_METHODS_DEFINE_PARAMETER_CONSTRAINTS, .addProperty(BaseHibernateValidatorConfiguration.ALLOW_PARALLEL_METHODS_DEFINE_PARAMETER_CONSTRAINTS,
"true") "true")
@@ -489,15 +537,17 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
final DistributionSetTagManagement distributionSetTagManagement, final SystemManagement systemManagement, final DistributionSetTagManagement distributionSetTagManagement, final SystemManagement systemManagement,
final DistributionSetTypeManagement distributionSetTypeManagement, final QuotaManagement quotaManagement, final DistributionSetTypeManagement distributionSetTypeManagement, final QuotaManagement quotaManagement,
final DistributionSetMetadataRepository distributionSetMetadataRepository, final DistributionSetMetadataRepository distributionSetMetadataRepository,
final TargetRepository targetRepository,
final TargetFilterQueryRepository targetFilterQueryRepository, final ActionRepository actionRepository, final TargetFilterQueryRepository targetFilterQueryRepository, final ActionRepository actionRepository,
final EventPublisherHolder eventPublisherHolder, final TenantAware tenantAware, final EventPublisherHolder eventPublisherHolder, final TenantAware tenantAware,
final VirtualPropertyReplacer virtualPropertyReplacer, final VirtualPropertyReplacer virtualPropertyReplacer,
final SoftwareModuleRepository softwareModuleRepository, final SoftwareModuleRepository softwareModuleRepository,
final DistributionSetTagRepository distributionSetTagRepository, final DistributionSetTagRepository distributionSetTagRepository,
final AfterTransactionCommitExecutor afterCommit, final JpaProperties properties) { final AfterTransactionCommitExecutor afterCommit,
final JpaProperties properties) {
return new JpaDistributionSetManagement(entityManager, distributionSetRepository, distributionSetTagManagement, return new JpaDistributionSetManagement(entityManager, distributionSetRepository, distributionSetTagManagement,
systemManagement, distributionSetTypeManagement, quotaManagement, distributionSetMetadataRepository, systemManagement, distributionSetTypeManagement, quotaManagement, distributionSetMetadataRepository,
targetFilterQueryRepository, actionRepository, eventPublisherHolder, tenantAware, targetRepository, targetFilterQueryRepository, actionRepository, eventPublisherHolder, tenantAware,
virtualPropertyReplacer, softwareModuleRepository, distributionSetTagRepository, afterCommit, virtualPropertyReplacer, softwareModuleRepository, distributionSetTagRepository, afterCommit,
properties.getDatabase()); properties.getDatabase());
@@ -571,11 +621,11 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
final TargetFilterQueryRepository targetFilterQueryRepository, final TargetFilterQueryRepository targetFilterQueryRepository,
final TargetTypeRepository targetTypeRepository, final TargetTagRepository targetTagRepository, final TargetTypeRepository targetTypeRepository, final TargetTagRepository targetTagRepository,
final EventPublisherHolder eventPublisherHolder, final TenantAware tenantAware, final EventPublisherHolder eventPublisherHolder, final TenantAware tenantAware,
final AfterTransactionCommitExecutor afterCommit, final VirtualPropertyReplacer virtualPropertyReplacer, final VirtualPropertyReplacer virtualPropertyReplacer,
final JpaProperties properties, final DistributionSetManagement distributionSetManagement) { final JpaProperties properties, final DistributionSetManagement distributionSetManagement) {
return new JpaTargetManagement(entityManager, distributionSetManagement, quotaManagement, targetRepository, return new JpaTargetManagement(entityManager, distributionSetManagement, quotaManagement, targetRepository,
targetTypeRepository, targetMetadataRepository, rolloutGroupRepository, targetFilterQueryRepository, targetTypeRepository, targetMetadataRepository, rolloutGroupRepository, targetFilterQueryRepository,
targetTagRepository, eventPublisherHolder, tenantAware, afterCommit, virtualPropertyReplacer, targetTagRepository, eventPublisherHolder, tenantAware, virtualPropertyReplacer,
properties.getDatabase()); properties.getDatabase());
} }
@@ -594,8 +644,6 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
* to access quotas * to access quotas
* @param properties * @param properties
* JPA properties * JPA properties
* @param tenantAware
* the {@link TenantAware} bean holding the tenant information
* *
* @return a new {@link TargetFilterQueryManagement} * @return a new {@link TargetFilterQueryManagement}
*/ */
@@ -606,12 +654,13 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
final VirtualPropertyReplacer virtualPropertyReplacer, final VirtualPropertyReplacer virtualPropertyReplacer,
final DistributionSetManagement distributionSetManagement, final QuotaManagement quotaManagement, final DistributionSetManagement distributionSetManagement, final QuotaManagement quotaManagement,
final JpaProperties properties, final TenantConfigurationManagement tenantConfigurationManagement, final JpaProperties properties, final TenantConfigurationManagement tenantConfigurationManagement,
final SystemSecurityContext systemSecurityContext, final TenantAware tenantAware) { final SystemSecurityContext systemSecurityContext, final ContextAware contextAware) {
return new JpaTargetFilterQueryManagement(targetFilterQueryRepository, targetManagement, return new JpaTargetFilterQueryManagement(targetFilterQueryRepository, targetManagement,
virtualPropertyReplacer, distributionSetManagement, quotaManagement, properties.getDatabase(), virtualPropertyReplacer, distributionSetManagement, quotaManagement, properties.getDatabase(),
tenantConfigurationManagement, systemSecurityContext, tenantAware); tenantConfigurationManagement, systemSecurityContext, contextAware);
} }
/** /**
* {@link JpaTargetTagManagement} bean. * {@link JpaTargetTagManagement} bean.
* *
@@ -654,10 +703,12 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
final SoftwareModuleMetadataRepository softwareModuleMetadataRepository, final SoftwareModuleMetadataRepository softwareModuleMetadataRepository,
final SoftwareModuleTypeRepository softwareModuleTypeRepository, final AuditorAware<String> auditorProvider, final SoftwareModuleTypeRepository softwareModuleTypeRepository, final AuditorAware<String> auditorProvider,
final ArtifactManagement artifactManagement, final QuotaManagement quotaManagement, final ArtifactManagement artifactManagement, final QuotaManagement quotaManagement,
final VirtualPropertyReplacer virtualPropertyReplacer, final JpaProperties properties) { final VirtualPropertyReplacer virtualPropertyReplacer,
final JpaProperties properties) {
return new JpaSoftwareModuleManagement(entityManager, distributionSetRepository, softwareModuleRepository, return new JpaSoftwareModuleManagement(entityManager, distributionSetRepository, softwareModuleRepository,
softwareModuleMetadataRepository, softwareModuleTypeRepository, auditorProvider, artifactManagement, softwareModuleMetadataRepository, softwareModuleTypeRepository, auditorProvider, artifactManagement,
quotaManagement, virtualPropertyReplacer, properties.getDatabase()); quotaManagement, virtualPropertyReplacer,
properties.getDatabase());
} }
/** /**
@@ -671,17 +722,20 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
final DistributionSetTypeRepository distributionSetTypeRepository, final DistributionSetTypeRepository distributionSetTypeRepository,
final SoftwareModuleTypeRepository softwareModuleTypeRepository, final SoftwareModuleTypeRepository softwareModuleTypeRepository,
final VirtualPropertyReplacer virtualPropertyReplacer, final VirtualPropertyReplacer virtualPropertyReplacer,
final SoftwareModuleRepository softwareModuleRepository, final JpaProperties properties) { final SoftwareModuleRepository softwareModuleRepository,
final JpaProperties properties) {
return new JpaSoftwareModuleTypeManagement(distributionSetTypeRepository, softwareModuleTypeRepository, return new JpaSoftwareModuleTypeManagement(distributionSetTypeRepository, softwareModuleTypeRepository,
virtualPropertyReplacer, softwareModuleRepository, properties.getDatabase()); virtualPropertyReplacer, softwareModuleRepository,
properties.getDatabase());
} }
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
RolloutHandler rolloutHandler(final TenantAware tenantAware, final RolloutManagement rolloutManagement, RolloutHandler rolloutHandler(final TenantAware tenantAware, final RolloutManagement rolloutManagement,
final RolloutExecutor rolloutExecutor, final LockRegistry lockRegistry, final RolloutExecutor rolloutExecutor, final LockRegistry lockRegistry,
final PlatformTransactionManager txManager) { final PlatformTransactionManager txManager, final ContextAware contextAware) {
return new JpaRolloutHandler(tenantAware, rolloutManagement, rolloutExecutor, lockRegistry, txManager); return new JpaRolloutHandler(tenantAware, rolloutManagement, rolloutExecutor, lockRegistry, txManager,
contextAware);
} }
@Bean @Bean
@@ -708,10 +762,12 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
final VirtualPropertyReplacer virtualPropertyReplacer, final JpaProperties properties, final VirtualPropertyReplacer virtualPropertyReplacer, final JpaProperties properties,
final RolloutApprovalStrategy rolloutApprovalStrategy, final RolloutApprovalStrategy rolloutApprovalStrategy,
final TenantConfigurationManagement tenantConfigurationManagement, final TenantConfigurationManagement tenantConfigurationManagement,
final SystemSecurityContext systemSecurityContext) { final SystemSecurityContext systemSecurityContext,
final ContextAware contextAware) {
return new JpaRolloutManagement(targetManagement, distributionSetManagement, eventPublisherHolder, return new JpaRolloutManagement(targetManagement, distributionSetManagement, eventPublisherHolder,
virtualPropertyReplacer, properties.getDatabase(), rolloutApprovalStrategy, virtualPropertyReplacer, properties.getDatabase(), rolloutApprovalStrategy,
tenantConfigurationManagement, systemSecurityContext); tenantConfigurationManagement, systemSecurityContext,
contextAware);
} }
/** /**
@@ -792,10 +848,11 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
ArtifactManagement artifactManagement(final LocalArtifactRepository localArtifactRepository, ArtifactManagement artifactManagement(
final EntityManager entityManager, final LocalArtifactRepository localArtifactRepository,
final SoftwareModuleRepository softwareModuleRepository, final ArtifactRepository artifactRepository, final SoftwareModuleRepository softwareModuleRepository, final ArtifactRepository artifactRepository,
final QuotaManagement quotaManagement, final TenantAware tenantAware) { final QuotaManagement quotaManagement, final TenantAware tenantAware) {
return new JpaArtifactManagement(localArtifactRepository, softwareModuleRepository, artifactRepository, return new JpaArtifactManagement(entityManager, localArtifactRepository, softwareModuleRepository, artifactRepository,
quotaManagement, tenantAware); quotaManagement, tenantAware);
} }
@@ -853,24 +910,22 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
@ConditionalOnMissingBean @ConditionalOnMissingBean
AutoAssignExecutor autoAssignExecutor(final TargetFilterQueryManagement targetFilterQueryManagement, AutoAssignExecutor autoAssignExecutor(final TargetFilterQueryManagement targetFilterQueryManagement,
final TargetManagement targetManagement, final DeploymentManagement deploymentManagement, final TargetManagement targetManagement, final DeploymentManagement deploymentManagement,
final PlatformTransactionManager transactionManager, final TenantAware tenantAware) { final PlatformTransactionManager transactionManager, final ContextAware contextAware) {
return new AutoAssignChecker(targetFilterQueryManagement, targetManagement, deploymentManagement, return new AutoAssignChecker(targetFilterQueryManagement, targetManagement, deploymentManagement,
transactionManager, tenantAware); transactionManager, contextAware);
} }
/** /**
* {@link AutoAssignScheduler} bean. * {@link AutoAssignScheduler} bean.
* * <p/>
* Note: does not activate in test profile, otherwise it is hard to test the * Note: does not activate in test profile, otherwise it is hard to test the
* auto assign functionality. * auto assign functionality.
* *
* @param tenantAware
* to run as specific tenant
* @param systemManagement * @param systemManagement
* to find all tenants * to find all tenants
* @param systemSecurityContext * @param systemSecurityContext
* to run as system * to run as system
* @param autoAssignChecker * @param autoAssignExecutor
* to run a check as tenant * to run a check as tenant
* @param lockRegistry * @param lockRegistry
* to lock the tenant for auto assignment * to lock the tenant for auto assignment
@@ -930,7 +985,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/** /**
* {@link RolloutScheduler} bean. * {@link RolloutScheduler} bean.
* * <p/>
* Note: does not activate in test profile, otherwise it is hard to test the * Note: does not activate in test profile, otherwise it is hard to test the
* rollout handling functionality. * rollout handling functionality.
* *
@@ -946,7 +1001,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
@ConditionalOnMissingBean @ConditionalOnMissingBean
@Profile("!test") @Profile("!test")
@ConditionalOnProperty(prefix = "hawkbit.rollout.scheduler", name = "enabled", matchIfMissing = true) @ConditionalOnProperty(prefix = "hawkbit.rollout.scheduler", name = "enabled", matchIfMissing = true)
RolloutScheduler rolloutScheduler(final TenantAware tenantAware, final SystemManagement systemManagement, RolloutScheduler rolloutScheduler(final SystemManagement systemManagement,
final RolloutHandler rolloutHandler, final SystemSecurityContext systemSecurityContext) { final RolloutHandler rolloutHandler, final SystemSecurityContext systemSecurityContext) {
return new RolloutScheduler(systemManagement, rolloutHandler, systemSecurityContext); return new RolloutScheduler(systemManagement, rolloutHandler, systemSecurityContext);
} }
@@ -982,16 +1037,18 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
JpaDistributionSetInvalidationManagement distributionSetInvalidationManagement( JpaDistributionSetInvalidationManagement distributionSetInvalidationManagement(
final DistributionSetManagement distributionSetManagement, final RolloutManagement rolloutManagement, final DistributionSetManagement distributionSetManagement, final RolloutManagement rolloutManagement,
final DeploymentManagement deploymentManagement, final DeploymentManagement deploymentManagement,
final TargetFilterQueryManagement targetFilterQueryManagement, final PlatformTransactionManager txManager, final TargetFilterQueryManagement targetFilterQueryManagement, final ActionRepository actionRepository,
final PlatformTransactionManager txManager,
final RepositoryProperties repositoryProperties, final TenantAware tenantAware, final RepositoryProperties repositoryProperties, final TenantAware tenantAware,
final LockRegistry lockRegistry) { final LockRegistry lockRegistry) {
return new JpaDistributionSetInvalidationManagement(distributionSetManagement, rolloutManagement, return new JpaDistributionSetInvalidationManagement(distributionSetManagement, rolloutManagement,
deploymentManagement, targetFilterQueryManagement, txManager, repositoryProperties, tenantAware, deploymentManagement, targetFilterQueryManagement, actionRepository,
txManager, repositoryProperties, tenantAware,
lockRegistry); lockRegistry);
} }
/** /**
* Our default {@link BaseRepositoryTypeProvider} bean always provides the * Default {@link BaseRepositoryTypeProvider} bean always provides the
* NoCountBaseRepository * NoCountBaseRepository
* *
* @return a {@link BaseRepositoryTypeProvider} bean * @return a {@link BaseRepositoryTypeProvider} bean
@@ -999,14 +1056,13 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
BaseRepositoryTypeProvider baseRepositoryTypeProvider() { BaseRepositoryTypeProvider baseRepositoryTypeProvider() {
return new NoCountBaseRepositoryTypeProvider(); return new HawkBitBaseRepository.RepositoryTypeProvider();
} }
/** /**
* Default artifact encryption service bean that internally uses * Default artifact encryption service bean that internally uses
* {@link ArtifactEncryption} and {@link ArtifactEncryptionSecretsStore} * {@link ArtifactEncryption} and {@link ArtifactEncryptionSecretsStore} beans
* beans for {@link SoftwareModule} artifacts encryption/decryption * for {@link SoftwareModule} artifacts encryption/decryption
* *
* @return a {@link ArtifactEncryptionService} bean * @return a {@link ArtifactEncryptionService} bean
*/ */
@@ -1015,4 +1071,36 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
ArtifactEncryptionService artifactEncryptionService() { ArtifactEncryptionService artifactEncryptionService() {
return ArtifactEncryptionService.getInstance(); return ArtifactEncryptionService.getInstance();
} }
@Bean
public BeanPostProcessor entityManagerBeanPostProcessor(
@Autowired(required = false) final AccessController<JpaArtifact> artifactAccessController,
@Autowired(required = false) final AccessController<JpaSoftwareModuleType> softwareModuleTypeAccessController,
@Autowired(required = false) final AccessController<JpaSoftwareModule> softwareModuleAccessController,
@Autowired(required = false) final AccessController<JpaDistributionSetType> distributionSetTypeAccessController,
@Autowired(required = false) final AccessController<JpaDistributionSet> distributionSetAccessController,
@Autowired(required = false) final AccessController<JpaTargetType> targetTypeAccessControlManager,
@Autowired(required = false) final AccessController<JpaTarget> targetAccessControlManager) {
return new BeanPostProcessor() {
@Override
public Object postProcessAfterInitialization(@NonNull final Object bean, @NonNull final String beanName) throws BeansException {
if (bean instanceof LocalArtifactRepository repo) {
return repo.withACM(artifactAccessController);
} else if (bean instanceof SoftwareModuleTypeRepository repo) {
return repo.withACM(softwareModuleTypeAccessController);
} else if (bean instanceof SoftwareModuleRepository repo) {
return repo.withACM(softwareModuleAccessController);
} else if (bean instanceof DistributionSetTypeRepository repo) {
return repo.withACM(distributionSetTypeAccessController);
} else if (bean instanceof DistributionSetRepository repo) {
return repo.withACM(distributionSetAccessController);
} else if (bean instanceof TargetTypeRepository repo) {
return repo.withACM(targetTypeAccessControlManager);
} else if (bean instanceof TargetRepository repo) {
return repo.withACM(targetAccessControlManager);
}
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
};
}
} }

View File

@@ -1,65 +0,0 @@
/**
* Copyright (c) 2021 Bosch.IO GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa;
import java.io.Serializable;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.lang.Nullable;
/**
* Repository implementation that allows findAll with disabled count query.
*
* @param <T>
* entity type
* @param <I>
* key or ID type
*/
public class SimpleJpaWithNoCountRepository<T, I extends Serializable> extends SimpleJpaRepository<T, I>
implements NoCountSliceRepository<T> {
public SimpleJpaWithNoCountRepository(final Class<T> domainClass, final EntityManager em) {
super(domainClass, em);
}
public SimpleJpaWithNoCountRepository(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) {
super(entityInformation, entityManager);
}
@Override
public Slice<T> findAllWithoutCount(@Nullable Specification<T> spec, Pageable pageable) {
TypedQuery<T> query = getQuery(spec, pageable);
return pageable.isUnpaged() ? new PageImpl<>(query.getResultList()) : readPageWithoutCount(query, pageable);
}
@Override
public Slice<T> findAllWithoutCount(final Pageable pageable) {
return findAllWithoutCount(null, pageable);
}
protected <S extends T> Page<S> readPageWithoutCount(final TypedQuery<S> query, final Pageable pageable) {
query.setFirstResult((int) pageable.getOffset());
query.setMaxResults(pageable.getPageSize());
final List<S> content = query.getResultList();
return new PageImpl<>(content, pageable, content.size());
}
}

View File

@@ -1,140 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa;
import java.util.List;
import java.util.Optional;
import javax.persistence.EntityManager;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;
/**
* {@link SoftwareModule} repository.
*
*/
@Transactional(readOnly = true)
public interface SoftwareModuleRepository
extends BaseEntityRepository<JpaSoftwareModule, Long>, JpaSpecificationExecutor<JpaSoftwareModule> {
/**
* Counts all {@link SoftwareModule}s based on the given {@link Type}.
*
* @param type
* to count for
* @return number of {@link SoftwareModule}s
*/
Long countByType(JpaSoftwareModuleType type);
/**
* Retrieves {@link SoftwareModule} by filtering on name AND version AND
* type (which is unique per tenant.
*
* @param name
* to be filtered on
* @param version
* to be filtered on
* @param typeId
* to be filtered on
* @return the found {@link SoftwareModule} with the given name AND version
* AND type
*/
Optional<SoftwareModule> findOneByNameAndVersionAndTypeId(String name, String version, Long typeId);
/**
* deletes the {@link SoftwareModule}s with the given IDs.
*
* @param modifiedAt
* current timestamp
* @param modifiedBy
* user name of current auditor
* @param ids
* to be deleted
*
*/
@Modifying
@Transactional
@Query("UPDATE JpaSoftwareModule b SET b.deleted = 1, b.lastModifiedAt = :lastModifiedAt, b.lastModifiedBy = :lastModifiedBy WHERE b.id IN :ids")
void deleteSoftwareModule(@Param("lastModifiedAt") Long modifiedAt, @Param("lastModifiedBy") String modifiedBy,
@Param("ids") Long... ids);
/**
* @param pageable
* the page request to page the result set
* @param setId
* to search for
* @return all {@link SoftwareModule}s that are assigned to given
* {@link DistributionSet}.
*/
Page<SoftwareModule> findByAssignedToId(Pageable pageable, Long setId);
/**
* Count the software modules which are assigned to the distribution set
* with the given ID.
*
* @param setId
* the distribution set ID
*
* @return the number of software modules matching the given distribution
* set ID.
*/
long countByAssignedToId(Long setId);
/**
* @param pageable
* the page request to page the result set
* @param set
* to search for
* @param type
* to filter
* @return all {@link SoftwareModule}s that are assigned to given
* {@link DistributionSet} filtered by {@link SoftwareModuleType}.
*/
Page<SoftwareModule> findByAssignedToAndType(Pageable pageable, JpaDistributionSet set, SoftwareModuleType type);
/**
* retrieves all software modules with a given
* {@link SoftwareModule#getId()}.
*
* @param ids
* to search for
* @return {@link List} of found {@link SoftwareModule}s
*/
// Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477
@Query("SELECT sm FROM JpaSoftwareModule sm WHERE sm.id IN ?1")
List<JpaSoftwareModule> findByIdIn(Iterable<Long> ids);
/**
* Deletes all {@link TenantAwareBaseEntity} of a given tenant. For safety
* reasons (this is a "delete everything" query after all) we add the tenant
* manually to query even if this will by done by {@link EntityManager}
* anyhow. The DB should take care of optimizing this away.
*
* @param tenant
* to delete data from
*/
@Modifying
@Transactional
@Query("DELETE FROM JpaSoftwareModule t WHERE t.tenant = :tenant")
void deleteByTenant(@Param("tenant") String tenant);
}

View File

@@ -1,226 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import javax.persistence.EntityManager;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.lang.NonNull;
import org.springframework.transaction.annotation.Transactional;
/**
* {@link Target} repository.
*
*/
@Transactional(readOnly = true)
public interface TargetRepository extends BaseEntityRepository<JpaTarget, Long>, JpaSpecificationExecutor<JpaTarget> {
/**
* Sets {@link JpaTarget#getAssignedDistributionSet()}.
*
* @param set
* to use
* @param status
* to set
* @param modifiedAt
* current time
* @param modifiedBy
* current auditor
* @param targets
* to update
*/
@Modifying
@Transactional
@Query("UPDATE JpaTarget t SET t.assignedDistributionSet = :set, t.lastModifiedAt = :lastModifiedAt, t.lastModifiedBy = :lastModifiedBy, t.updateStatus = :status WHERE t.id IN :targets")
void setAssignedDistributionSetAndUpdateStatus(@Param("status") TargetUpdateStatus status,
@Param("set") JpaDistributionSet set, @Param("lastModifiedAt") Long modifiedAt,
@Param("lastModifiedBy") String modifiedBy, @Param("targets") Collection<Long> targets);
/**
* Sets {@link JpaTarget#getAssignedDistributionSet()},
* {@link JpaTarget#getInstalledDistributionSet()} and
* {@link JpaTarget#getInstallationDate()}
*
* @param set
* to use
* @param status
* to set
* @param modifiedAt
* current time
* @param modifiedBy
* current auditor
* @param targets
* to update
*/
@Modifying
@Transactional
@Query("UPDATE JpaTarget t SET t.assignedDistributionSet = :set, t.installedDistributionSet = :set, t.installationDate = :lastModifiedAt, t.lastModifiedAt = :lastModifiedAt, t.lastModifiedBy = :lastModifiedBy, t.updateStatus = :status WHERE t.id IN :targets")
void setAssignedAndInstalledDistributionSetAndUpdateStatus(@Param("status") TargetUpdateStatus status,
@Param("set") JpaDistributionSet set, @Param("lastModifiedAt") Long modifiedAt,
@Param("lastModifiedBy") String modifiedBy, @Param("targets") Collection<Long> targets);
/**
* Deletes the {@link Target}s with the given target IDs.
*
* @param targetIDs
* to be deleted
*/
@Modifying
@Transactional
// Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477
@Query("DELETE FROM JpaTarget t WHERE t.id IN ?1")
void deleteByIdIn(Collection<Long> targetIDs);
/**
* Finds all {@link Target}s in the repository.
*
* Calls version with (empty) spec to allow injecting further specs
*
* @return {@link List} of {@link Target}s
*/
@Override
@NonNull
default List<JpaTarget> findAll() {
return this.findAll(Specification.where(null));
}
/**
* Finds all {@link Target}s in the repository sorted.
*
* Calls version with (empty) spec to allow injecting further specs
*
* @param sort instructions to sort result by
* @return {@link List} of {@link Target}s
*/
@Override
@NonNull
default Iterable<JpaTarget> findAll(@NonNull Sort sort) {
return this.findAll(Specification.where(null), sort);
}
/**
* Finds a page of {@link Target}s in the repository.
*
* Calls version with (empty) spec to allow injecting further specs
*
* @param pageable paging context
* @return {@link List} of {@link Target}s
*/
@Override
@NonNull
default Page<JpaTarget> findAll(@NonNull Pageable pageable) {
return this.findAll(Specification.where(null), pageable);
}
/**
* Finds {@link Target}s in the repository matching a list of ids.
*
* Calls version based on spec to allow injecting further specs
*
* @param ids ids to filter for
* @return {@link List} of {@link Target}s
*/
@Override
@NonNull
default List<JpaTarget> findAllById(Iterable<Long> ids) {
final List<Long> collectedIds = StreamSupport.stream(ids.spliterator(), true).collect(Collectors.toList());
return this.findAll(Specification.where(TargetSpecifications.hasIdIn(collectedIds)));
}
/**
* Finds {@link Target} in the repository matching an id.
*
* Calls version based on spec to allow injecting further specs
*
* @param id id to filter for
* @return {@link Optional} of {@link Target}
*/
@Override
@NonNull
default Optional<JpaTarget> findById(@NonNull Long id) {
return this.findOne(Specification.where(TargetSpecifications.hasId(id)));
}
/**
* Checks whether {@link Target} in the repository matching an id exists or not.
*
* Calls version based on spec to allow injecting further specs
*
* @param id id to check for
* @return true if target with id exists
*/
@Override
default boolean existsById(@NonNull Long id) {
return this.exists(TargetSpecifications.hasId(id));
}
/**
* Checks whether {@link Target} in the repository matching a spec exists or not.
*
* @param spec to check for existence
* @return true if target with id exists
*/
default boolean exists(@NonNull Specification<JpaTarget> spec) {
return this.count(spec) > 0;
}
/**
* Count number of {@link Target}s in the repository.
*
* Calls version with an empty spec to allow injecting further specs
*
* @return number of targets in the repository
*/
@Override
default long count() {
return this.count(Specification.where(null));
}
/**
* Counts {@link Target} instances of given type in the repository.
*
* @param targetTypeId
* to search for
* @return number of found {@link Target}s
*/
long countByTargetTypeId(Long targetTypeId);
/**
* Deletes all {@link TenantAwareBaseEntity} of a given tenant. For safety
* reasons (this is a "delete everything" query after all) we add the tenant
* manually to query even if this will by done by {@link EntityManager} anyhow.
* The DB should take care of optimizing this away.
*
* @param tenant
* to delete data from
*/
@Modifying
@Transactional
@Query("DELETE FROM JpaTarget t WHERE t.tenant = :tenant")
void deleteByTenant(@Param("tenant") String tenant);
}

View File

@@ -1,206 +0,0 @@
/**
* Copyright (c) 2021 Bosch.IO GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetTypeSpecification;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.lang.NonNull;
import org.springframework.transaction.annotation.Transactional;
/**
* {@link PagingAndSortingRepository} and {@link org.springframework.data.repository.CrudRepository} for
* {@link JpaTargetType}.
*
*/
@Transactional(readOnly = true)
public interface TargetTypeRepository
extends BaseEntityRepository<JpaTargetType, Long>, JpaSpecificationExecutor<JpaTargetType> {
/**
* Finds {@link TargetType} in the repository matching an id.
*
* Calls version based on spec to allow injecting further specs
*
* @param id
* id to filter for
* @return {@link Optional} of {@link TargetType}
*/
@Override
@NonNull
default Optional<JpaTargetType> findById(@NonNull final Long id) {
return this.findOne(Specification.where(TargetTypeSpecification.hasId(id)));
}
/**
* @param ids
* List of ID
* @return Target type list
*/
@Override
@NonNull
default List<JpaTargetType> findAllById(final Iterable<Long> ids) {
final List<Long> collectedIds = StreamSupport.stream(ids.spliterator(), true).collect(Collectors.toList());
return this.findAll(Specification.where(TargetTypeSpecification.hasIdIn(collectedIds)));
}
/**
* Deletes the {@link TargetType}s with the given target IDs.
*
* @param targetTypeIDs
* to be deleted
*/
@Modifying
@Transactional
@Query("DELETE FROM JpaTargetType t WHERE t.id IN ?1")
void deleteByIdIn(Collection<Long> targetTypeIDs);
/**
* Finds all {@link TargetType}s in the repository.
*
* Calls version with (empty) spec to allow injecting further specs
*
* @return {@link List} of {@link TargetType}s
*/
@Override
@NonNull
default List<JpaTargetType> findAll() {
return this.findAll(Specification.where(null));
}
/**
* Finds all {@link TargetType}s in the repository sorted.
*
* Calls version with (empty) spec to allow injecting further specs
*
* @param sort
* instructions to sort result by
* @return {@link List} of {@link TargetType}s
*/
@Override
@NonNull
default Iterable<JpaTargetType> findAll(@NonNull final Sort sort) {
return this.findAll(Specification.where(null), sort);
}
/**
* Finds a page of {@link TargetType}s in the repository.
*
* Calls version with (empty) spec to allow injecting further specs
*
* @param pageable
* paging context
* @return {@link List} of {@link TargetType}s
*/
@Override
@NonNull
default Page<JpaTargetType> findAll(@NonNull final Pageable pageable) {
return this.findAll(Specification.where(null), pageable);
}
/**
* Checks whether {@link TargetType} in the repository matching an id exists
* or not.
*
* Calls version based on spec to allow injecting further specs
*
* @param id
* id to check for
* @return true if TargetType with id exists
*/
@Override
default boolean existsById(@NonNull final Long id) {
return this.exists(TargetTypeSpecification.hasId(id));
}
/**
* Checks whether {@link TargetType} in the repository matching a spec
* exists or not.
*
* @param spec
* to check for existence
* @return true if target with id exists
*/
default boolean exists(@NonNull final Specification<JpaTargetType> spec) {
return this.count(spec) > 0;
}
/**
* Count number of {@link TargetType}s in the repository.
*
* Calls version with an empty spec to allow injecting further specs
*
* @return number of targetTypes in the repository
*/
@Override
default long count() {
return this.count(Specification.where(null));
}
/**
* @param tenant
* Tenant
*/
@Modifying
@Transactional
@Query("DELETE FROM JpaTargetType t WHERE t.tenant = :tenant")
void deleteByTenant(@Param("tenant") String tenant);
@Query(value = "SELECT COUNT (t.id) FROM JpaDistributionSetType t JOIN t.compatibleToTargetTypes tt WHERE tt.id = :id")
long countDsSetTypesById(@Param("id") Long id);
/**
*
* @param dsTypeId
* to search for
* @return all {@link TargetType}s in the repository with given
* {@link TargetType#getName()}
*/
default List<JpaTargetType> findByDsType(@Param("id") final Long dsTypeId) {
return this.findAll(Specification.where(TargetTypeSpecification.hasDsSetType(dsTypeId)));
}
/**
*
* @param name
* to search for
* @return all {@link TargetType}s in the repository with given
* {@link TargetType#getName()}
*/
default Optional<JpaTargetType> findByName(final String name) {
return this.findOne(Specification.where(TargetTypeSpecification.hasName(name)));
}
/**
* Count number of {@link TargetType}s in the repository by name.
*
* @param name
* target type name
* @return number of targetTypes in the repository by name
*/
default long countByName(final String name) {
return this.count(TargetTypeSpecification.hasName(name));
}
}

View File

@@ -0,0 +1,100 @@
/**
* Copyright (c) 2023 Bosch.IO GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.acm;
import java.util.Optional;
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.lang.Nullable;
/**
* Interface of an extended access control by providing means or fine-grained access control.
* Used by repository (and on few places by management) layer to verify the permission for CRUD operations
* based on some access criteria.
* <p/>
* First the basic service based access control (hawkBit permissions) on the management layer is applied then
* additional restrictions (e.g. entity based) could be applied.
* <p/>
* <b>Note</b>: Experimental, only
*
* @param <T> the domain type the repository manages
*/
public interface AccessController<T> {
/**
* Introduce a new specification to limit the access to a specific entity.
*
* @return a new specification limiting the access, if empty no access restrictions
* are to be applied
*/
Optional<Specification<T>> getAccessRules(Operation operation);
/**
* Append the resource limitation on an already existing specification.
*
* @param specification
* is the root specification which needs to be appended by the
* resource limitation
* @return a new appended specification
*/
@Nullable
default Specification<T> appendAccessRules(final Operation operation, @Nullable final Specification<T> specification) {
return getAccessRules(operation)
.map(accessRules -> specification == null ? accessRules : specification.and(accessRules))
.orElse(specification);
}
/**
* Verify if the given {@link Operation} is permitted for the provided entity.
*
* @throws InsufficientPermissionException
* if operation is not permitted for given entities
*/
void assertOperationAllowed(final Operation operation, final T entity) throws InsufficientPermissionException;
/**
* Verify if the given {@link Operation} is permitted for the provided entities.
*
* @throws InsufficientPermissionException
* if operation is not permitted for given entities
*/
default void assertOperationAllowed(final Operation operation, final Iterable<? extends T> entities) throws InsufficientPermissionException {
for (final T entity : entities) {
assertOperationAllowed(operation, entity);
}
}
/**
* Enum to define the perform operation to verify
*/
enum Operation {
/**
* Entity creation
*/
CREATE,
/**
* Read entities
*/
READ,
/**
* Entity modification (e.g. name/description change, tag/type assignment, etc.)
*/
UPDATE,
/**
* Entity deletion
*/
DELETE
}
}

View File

@@ -75,7 +75,7 @@ public class ExceptionMappingAspectHandler implements Ordered {
* the thrown and catched exception * the thrown and catched exception
* @throws Throwable * @throws Throwable
*/ */
@AfterThrowing(pointcut = "execution( * org.eclipse.hawkbit.repository.jpa.*Management.*(..))", throwing = "ex") @AfterThrowing(pointcut = "execution( * org.eclipse.hawkbit.repository.jpa.management.*Management.*(..))", throwing = "ex")
// Exception for squid:S00112, squid:S1162 // Exception for squid:S00112, squid:S1162
// It is a AspectJ proxy which deals with exceptions. // It is a AspectJ proxy which deals with exceptions.
@SuppressWarnings({ "squid:S00112", "squid:S1162" }) @SuppressWarnings({ "squid:S00112", "squid:S1162" })

View File

@@ -10,10 +10,9 @@
package org.eclipse.hawkbit.repository.jpa.autoassign; package org.eclipse.hawkbit.repository.jpa.autoassign;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.ContextAware;
import org.eclipse.hawkbit.repository.DeploymentManagement; import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement; import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.autoassign.AutoAssignExecutor; import org.eclipse.hawkbit.repository.autoassign.AutoAssignExecutor;
@@ -39,8 +38,8 @@ public abstract class AbstractAutoAssignExecutor implements AutoAssignExecutor {
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractAutoAssignExecutor.class); private static final Logger LOGGER = LoggerFactory.getLogger(AbstractAutoAssignExecutor.class);
/** /**
* The message which is added to the action status when a distribution set * The message which is added to the action status when a distribution set is
* is assigned to an target. First %s is the name of the target filter. * assigned to an target. First %s is the name of the target filter.
*/ */
private static final String ACTION_MESSAGE = "Auto assignment by target filter: %s"; private static final String ACTION_MESSAGE = "Auto assignment by target filter: %s";
@@ -55,7 +54,7 @@ public abstract class AbstractAutoAssignExecutor implements AutoAssignExecutor {
private final PlatformTransactionManager transactionManager; private final PlatformTransactionManager transactionManager;
private final TenantAware tenantAware; private final ContextAware contextAware;
/** /**
* Constructor * Constructor
@@ -66,20 +65,16 @@ public abstract class AbstractAutoAssignExecutor implements AutoAssignExecutor {
* to assign distribution sets to targets * to assign distribution sets to targets
* @param transactionManager * @param transactionManager
* to run transactions * to run transactions
* @param tenantAware * @param contextAware
* to handle the tenant context * to handle the context
*/ */
protected AbstractAutoAssignExecutor(final TargetFilterQueryManagement targetFilterQueryManagement, protected AbstractAutoAssignExecutor(final TargetFilterQueryManagement targetFilterQueryManagement,
final DeploymentManagement deploymentManagement, final PlatformTransactionManager transactionManager, final DeploymentManagement deploymentManagement, final PlatformTransactionManager transactionManager,
final TenantAware tenantAware) { final ContextAware contextAware) {
this.targetFilterQueryManagement = targetFilterQueryManagement; this.targetFilterQueryManagement = targetFilterQueryManagement;
this.deploymentManagement = deploymentManagement; this.deploymentManagement = deploymentManagement;
this.transactionManager = transactionManager; this.transactionManager = transactionManager;
this.tenantAware = tenantAware; this.contextAware = contextAware;
}
protected TargetFilterQueryManagement getTargetFilterQueryManagement() {
return targetFilterQueryManagement;
} }
protected DeploymentManagement getDeploymentManagement() { protected DeploymentManagement getDeploymentManagement() {
@@ -90,10 +85,13 @@ public abstract class AbstractAutoAssignExecutor implements AutoAssignExecutor {
return transactionManager; return transactionManager;
} }
protected TenantAware getTenantAware() { protected TenantAware getContextAware() {
return tenantAware; return contextAware;
} }
// run in the context the auto assignment is made in, i.e. if there is access control context it runs in it
// otherwise in the tenant & user context built by createdBy
// Note! It must be called in a tenant context, i.e. contextAware.getCurrentTenant() returns the tenant
protected void forEachFilterWithAutoAssignDS(final Consumer<TargetFilterQuery> consumer) { protected void forEachFilterWithAutoAssignDS(final Consumer<TargetFilterQuery> consumer) {
Slice<TargetFilterQuery> filterQueries; Slice<TargetFilterQuery> filterQueries;
Pageable query = PageRequest.of(0, PAGE_SIZE); Pageable query = PageRequest.of(0, PAGE_SIZE);
@@ -103,7 +101,19 @@ public abstract class AbstractAutoAssignExecutor implements AutoAssignExecutor {
filterQueries.forEach(filterQuery -> { filterQueries.forEach(filterQuery -> {
try { try {
runInUserContext(filterQuery, () -> consumer.accept(filterQuery)); filterQuery.getAccessControlContext().ifPresentOrElse(
context -> // has stored context - executes it with it
contextAware.runInContext(
context,
() -> consumer.accept(filterQuery)),
() -> // has no stored context - executes it in the tenant & user scope
contextAware.runAsTenantAsUser(
contextAware.getCurrentTenant(),
getAutoAssignmentInitiatedBy(filterQuery), () -> {
consumer.accept(filterQuery);
return null;
})
);
} catch (final RuntimeException ex) { } catch (final RuntimeException ex) {
LOGGER.debug( LOGGER.debug(
"Exception on forEachFilterWithAutoAssignDS execution for tenant {} with filter id {}. Continue with next filter query.", "Exception on forEachFilterWithAutoAssignDS execution for tenant {} with filter id {}. Continue with next filter query.",
@@ -118,8 +128,8 @@ public abstract class AbstractAutoAssignExecutor implements AutoAssignExecutor {
} }
/** /**
* Runs target assignments within a dedicated transaction for a given list * Runs target assignments within a dedicated transaction for a given list of
* of controllerIDs * controllerIDs
* *
* @param targetFilterQuery * @param targetFilterQuery
* the target filter query * the target filter query
@@ -147,8 +157,8 @@ public abstract class AbstractAutoAssignExecutor implements AutoAssignExecutor {
} }
/** /**
* Creates a list of {@link DeploymentRequest} for given list of * Creates a list of {@link DeploymentRequest} for given list of controllerIds
* controllerIds and {@link TargetFilterQuery} * and {@link TargetFilterQuery}
* *
* @param controllerIds * @param controllerIds
* list of controllerIds * list of controllerIds
@@ -169,16 +179,12 @@ public abstract class AbstractAutoAssignExecutor implements AutoAssignExecutor {
.deploymentRequest(controllerId, filterQuery.getAutoAssignDistributionSet().getId()) .deploymentRequest(controllerId, filterQuery.getAutoAssignDistributionSet().getId())
.setActionType(autoAssignActionType).setWeight(filterQuery.getAutoAssignWeight().orElse(null)) .setActionType(autoAssignActionType).setWeight(filterQuery.getAutoAssignWeight().orElse(null))
.setConfirmationRequired(filterQuery.isConfirmationRequired()).build()) .setConfirmationRequired(filterQuery.isConfirmationRequired()).build())
.collect(Collectors.toList()); .toList();
}
protected void runInUserContext(final TargetFilterQuery targetFilterQuery, final Runnable handler) {
DeploymentHelper.runInNonSystemContext(handler,
() -> Objects.requireNonNull(getAutoAssignmentInitiatedBy(targetFilterQuery)), tenantAware);
} }
protected static String getAutoAssignmentInitiatedBy(final TargetFilterQuery targetFilterQuery) { protected static String getAutoAssignmentInitiatedBy(final TargetFilterQuery targetFilterQuery) {
return StringUtils.isEmpty(targetFilterQuery.getAutoAssignInitiatedBy()) ? targetFilterQuery.getCreatedBy() return StringUtils.hasText(targetFilterQuery.getAutoAssignInitiatedBy())
: targetFilterQuery.getAutoAssignInitiatedBy(); ? targetFilterQuery.getAutoAssignInitiatedBy()
: targetFilterQuery.getCreatedBy();
} }
} }

View File

@@ -11,10 +11,10 @@ package org.eclipse.hawkbit.repository.jpa.autoassign;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
import javax.persistence.PersistenceException; import javax.persistence.PersistenceException;
import org.eclipse.hawkbit.ContextAware;
import org.eclipse.hawkbit.exception.AbstractServerRtException; import org.eclipse.hawkbit.exception.AbstractServerRtException;
import org.eclipse.hawkbit.repository.DeploymentManagement; import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement; import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
@@ -22,7 +22,6 @@ import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants; import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery; import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
@@ -54,36 +53,36 @@ public class AutoAssignChecker extends AbstractAutoAssignExecutor {
* to assign distribution sets to targets * to assign distribution sets to targets
* @param transactionManager * @param transactionManager
* to run transactions * to run transactions
* @param tenantAware * @param contextAware
* to handle the tenant context * to handle the context
*/ */
public AutoAssignChecker(final TargetFilterQueryManagement targetFilterQueryManagement, public AutoAssignChecker(final TargetFilterQueryManagement targetFilterQueryManagement,
final TargetManagement targetManagement, final DeploymentManagement deploymentManagement, final TargetManagement targetManagement, final DeploymentManagement deploymentManagement,
final PlatformTransactionManager transactionManager, final TenantAware tenantAware) { final PlatformTransactionManager transactionManager, final ContextAware contextAware) {
super(targetFilterQueryManagement, deploymentManagement, transactionManager, tenantAware); super(targetFilterQueryManagement, deploymentManagement, transactionManager, contextAware);
this.targetManagement = targetManagement; this.targetManagement = targetManagement;
} }
@Override @Override
@Transactional(propagation = Propagation.REQUIRES_NEW) @Transactional(propagation = Propagation.REQUIRES_NEW)
public void checkAllTargets() { public void checkAllTargets() {
LOGGER.debug("Auto assign check call for tenant {} started", getTenantAware().getCurrentTenant()); LOGGER.debug("Auto assign check call for tenant {} started", getContextAware().getCurrentTenant());
forEachFilterWithAutoAssignDS(this::checkByTargetFilterQueryAndAssignDS); forEachFilterWithAutoAssignDS(this::checkByTargetFilterQueryAndAssignDS);
LOGGER.debug("Auto assign check call for tenant {} finished", getTenantAware().getCurrentTenant()); LOGGER.debug("Auto assign check call for tenant {} finished", getContextAware().getCurrentTenant());
} }
@Override @Override
public void checkSingleTarget(String controllerId) { public void checkSingleTarget(String controllerId) {
LOGGER.debug("Auto assign check call for tenant {} and device {} started", getTenantAware().getCurrentTenant(), LOGGER.debug("Auto assign check call for tenant {} and device {} started", getContextAware().getCurrentTenant(),
controllerId); controllerId);
forEachFilterWithAutoAssignDS(filter -> checkForDevice(controllerId, filter)); forEachFilterWithAutoAssignDS(filter -> checkForDevice(controllerId, filter));
LOGGER.debug("Auto assign check call for tenant {} and device {} finished", getTenantAware().getCurrentTenant(), LOGGER.debug("Auto assign check call for tenant {} and device {} finished", getContextAware().getCurrentTenant(),
controllerId); controllerId);
} }
/** /**
* Fetches the distribution set, gets all controllerIds and assigns the DS * Fetches the distribution set, gets all controllerIds and assigns the DS to
* to them. Catches PersistenceException and own exceptions derived from * them. Catches PersistenceException and own exceptions derived from
* AbstractServerRtException * AbstractServerRtException
* *
* @param targetFilterQuery * @param targetFilterQuery
@@ -91,34 +90,34 @@ public class AutoAssignChecker extends AbstractAutoAssignExecutor {
*/ */
private void checkByTargetFilterQueryAndAssignDS(final TargetFilterQuery targetFilterQuery) { private void checkByTargetFilterQueryAndAssignDS(final TargetFilterQuery targetFilterQuery) {
LOGGER.debug("Auto assign check call for tenant {} and target filter query id {} started", LOGGER.debug("Auto assign check call for tenant {} and target filter query id {} started",
getTenantAware().getCurrentTenant(), targetFilterQuery.getId()); getContextAware().getCurrentTenant(), targetFilterQuery.getId());
try { try {
int count; int count;
do { do {
final List<String> controllerIds = targetManagement final List<String> controllerIds = targetManagement
.findByTargetFilterQueryAndNonDSAndCompatible( .findByTargetFilterQueryAndNonDSAndCompatibleAndUpdatable(
PageRequest.of(0, Constants.MAX_ENTRIES_IN_STATEMENT), PageRequest.of(0, Constants.MAX_ENTRIES_IN_STATEMENT),
targetFilterQuery.getAutoAssignDistributionSet().getId(), targetFilterQuery.getQuery()) targetFilterQuery.getAutoAssignDistributionSet().getId(), targetFilterQuery.getQuery())
.getContent().stream().map(Target::getControllerId).collect(Collectors.toList()); .getContent().stream().map(Target::getControllerId).toList();
LOGGER.debug( LOGGER.debug(
"Retrieved {} auto assign targets for tenant {} and target filter query id {}, starting with assignment", "Retrieved {} auto assign targets for tenant {} and target filter query id {}, starting with assignment",
controllerIds.size(), getTenantAware().getCurrentTenant(), targetFilterQuery.getId()); controllerIds.size(), getContextAware().getCurrentTenant(), targetFilterQuery.getId());
count = runTransactionalAssignment(targetFilterQuery, controllerIds); count = runTransactionalAssignment(targetFilterQuery, controllerIds);
LOGGER.debug( LOGGER.debug(
"Assignment for {} auto assign targets for tenant {} and target filter query id {} finished", "Assignment for {} auto assign targets for tenant {} and target filter query id {} finished",
controllerIds.size(), getTenantAware().getCurrentTenant(), targetFilterQuery.getId()); controllerIds.size(), getContextAware().getCurrentTenant(), targetFilterQuery.getId());
} while (count == Constants.MAX_ENTRIES_IN_STATEMENT); } while (count == Constants.MAX_ENTRIES_IN_STATEMENT);
} catch (final PersistenceException | AbstractServerRtException e) { } catch (final PersistenceException | AbstractServerRtException e) {
LOGGER.error("Error during auto assign check of target filter query id {}", targetFilterQuery.getId(), e); LOGGER.error("Error during auto assign check of target filter query id {}", targetFilterQuery.getId(), e);
} }
LOGGER.debug("Auto assign check call for tenant {} and target filter query id {} finished", LOGGER.debug("Auto assign check call for tenant {} and target filter query id {} finished",
getTenantAware().getCurrentTenant(), targetFilterQuery.getId()); getContextAware().getCurrentTenant(), targetFilterQuery.getId());
} }
private void checkForDevice(final String controllerId, final TargetFilterQuery targetFilterQuery) { private void checkForDevice(final String controllerId, final TargetFilterQuery targetFilterQuery) {
LOGGER.debug("Auto assign check call for tenant {} and target filter query id {} for device {} started", LOGGER.debug("Auto assign check call for tenant {} and target filter query id {} for device {} started",
getTenantAware().getCurrentTenant(), targetFilterQuery.getId(), controllerId); getContextAware().getCurrentTenant(), targetFilterQuery.getId(), controllerId);
try { try {
final boolean controllerIdMatches = targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatible( final boolean controllerIdMatches = targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatible(
controllerId, targetFilterQuery.getAutoAssignDistributionSet().getId(), controllerId, targetFilterQuery.getAutoAssignDistributionSet().getId(),
@@ -132,6 +131,6 @@ public class AutoAssignChecker extends AbstractAutoAssignExecutor {
LOGGER.error("Error during auto assign check of target filter query id {}", targetFilterQuery.getId(), e); LOGGER.error("Error during auto assign check of target filter query id {}", targetFilterQuery.getId(), e);
} }
LOGGER.debug("Auto assign check call for tenant {} and target filter query id {} finished", LOGGER.debug("Auto assign check call for tenant {} and target filter query id {} finished",
getTenantAware().getCurrentTenant(), targetFilterQuery.getId()); getContextAware().getCurrentTenant(), targetFilterQuery.getId());
} }
} }

View File

@@ -58,7 +58,7 @@ public class AutoAssignScheduler {
/** /**
* Scheduler method called by the spring-async mechanism. Retrieves all * Scheduler method called by the spring-async mechanism. Retrieves all
* tenants from the {@link SystemManagement#findTenants()} and runs for each * tenants and runs for each
* tenant the auto assignments defined in the target filter queries * tenant the auto assignments defined in the target filter queries
* {@link SystemSecurityContext}. * {@link SystemSecurityContext}.
*/ */
@@ -83,7 +83,7 @@ public class AutoAssignScheduler {
} }
try { try {
LOGGER.debug("Auto assign scheduled execution has aquired lock and started for each tenant."); LOGGER.debug("Auto assign scheduled execution has acquired lock and started for each tenant.");
systemManagement.forEachTenant(tenant -> autoAssignExecutor.checkAllTargets()); systemManagement.forEachTenant(tenant -> autoAssignExecutor.checkAllTargets());
} finally { } finally {
lock.unlock(); lock.unlock();

View File

@@ -7,7 +7,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.management;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@@ -25,8 +25,14 @@ import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor; import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction; import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus; import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction_;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget; import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
import org.eclipse.hawkbit.repository.jpa.repository.ActionStatusRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper; import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.Status; import org.eclipse.hawkbit.repository.model.Action.Status;
@@ -42,6 +48,8 @@ import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import javax.persistence.criteria.JoinType;
/** /**
* {@link DistributionSet} to {@link Target} assignment strategy as utility for * {@link DistributionSet} to {@link Target} assignment strategy as utility for
* {@link JpaDeploymentManagement}. * {@link JpaDeploymentManagement}.
@@ -141,17 +149,23 @@ public abstract class AbstractDsAssignmentStrategy {
/** /**
* Cancels {@link Action}s that are no longer necessary and sends * Cancels {@link Action}s that are no longer necessary and sends
* cancellations to the controller. * cancellations to the controller.
* <p/>
* No access control applied
* *
* @param targetsIds * @param targetsIds to override {@link Action}s
* to override {@link Action}s
*/ */
protected List<Long> overrideObsoleteUpdateActions(final Collection<Long> targetsIds) { protected List<Long> overrideObsoleteUpdateActions(final Collection<Long> targetsIds) {
// Figure out if there are potential target/action combinations that // Figure out if there are potential target/action combinations that
// need to be considered for cancellation // need to be considered for cancellation
final List<JpaAction> activeActions = actionRepository final List<JpaAction> activeActions = actionRepository.findAll((root, query, cb) -> {
.findByActiveAndTargetIdInAndActionStatusNotEqualToAndDistributionSetNotRequiredMigrationStep( root.fetch(JpaAction_.target, JoinType.LEFT);
targetsIds, Action.Status.CANCELING); return cb.and(
cb.equal(root.get(JpaAction_.active), true),
cb.equal(root.get(JpaAction_.distributionSet).get(JpaDistributionSet_.requiredMigrationStep), false),
cb.notEqual(root.get(JpaAction_.status), Action.Status.CANCELING),
root.get(JpaAction_.target).get(JpaTarget_.id).in(targetsIds)
);
});
final List<Long> targetIds = activeActions.stream().map(action -> { final List<Long> targetIds = activeActions.stream().map(action -> {
action.setStatus(Status.CANCELING); action.setStatus(Status.CANCELING);
@@ -174,16 +188,22 @@ public abstract class AbstractDsAssignmentStrategy {
/** /**
* Closes {@link Action}s that are no longer necessary without sending a * Closes {@link Action}s that are no longer necessary without sending a
* hint to the controller. * hint to the controller.
* <p/>
* No access control applied
* *
* @param targetsIds * @param targetsIds to override {@link Action}s
* to override {@link Action}s
*/ */
protected List<Long> closeObsoleteUpdateActions(final Collection<Long> targetsIds) { protected List<Long> closeObsoleteUpdateActions(final Collection<Long> targetsIds) {
// Figure out if there are potential target/action combinations that // Figure out if there are potential target/action combinations that
// need to be considered for cancellation // need to be considered for cancellation
final List<JpaAction> activeActions = actionRepository final List<JpaAction> activeActions = actionRepository.findAll((root, query, cb) -> {
.findByActiveAndTargetIdInAndDistributionSetNotRequiredMigrationStep(targetsIds); root.fetch(JpaAction_.target, JoinType.LEFT);
return cb.and(
cb.equal(root.get(JpaAction_.active), true),
cb.equal(root.get(JpaAction_.distributionSet).get(JpaDistributionSet_.requiredMigrationStep), false),
root.get(JpaAction_.target).get(JpaTarget_.id).in(targetsIds)
);
});
return activeActions.stream().map(action -> { return activeActions.stream().map(action -> {
action.setStatus(Status.CANCELED); action.setStatus(Status.CANCELED);

View File

@@ -7,10 +7,9 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.management;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -21,6 +20,10 @@ import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.builder.JpaActionStatusCreate; import org.eclipse.hawkbit.repository.jpa.builder.JpaActionStatusCreate;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction; import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus; 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;
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper; import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.ActionStatus; import org.eclipse.hawkbit.repository.model.ActionStatus;
@@ -28,6 +31,7 @@ import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import static org.eclipse.hawkbit.repository.model.Action.ActionType.DOWNLOAD_ONLY; import static org.eclipse.hawkbit.repository.model.Action.ActionType.DOWNLOAD_ONLY;
import static org.eclipse.hawkbit.repository.model.Action.Status.FINISHED; import static org.eclipse.hawkbit.repository.model.Action.Status.FINISHED;
@@ -44,7 +48,7 @@ public class JpaActionManagement {
protected final QuotaManagement quotaManagement; protected final QuotaManagement quotaManagement;
protected final RepositoryProperties repositoryProperties; protected final RepositoryProperties repositoryProperties;
protected JpaActionManagement(final ActionRepository actionRepository, public JpaActionManagement(final ActionRepository actionRepository,
final ActionStatusRepository actionStatusRepository, final QuotaManagement quotaManagement, final ActionStatusRepository actionStatusRepository, final QuotaManagement quotaManagement,
final RepositoryProperties repositoryProperties) { final RepositoryProperties repositoryProperties) {
this.actionRepository = actionRepository; this.actionRepository = actionRepository;
@@ -53,18 +57,28 @@ public class JpaActionManagement {
this.repositoryProperties = repositoryProperties; this.repositoryProperties = repositoryProperties;
} }
protected List<Action> findActiveActionsWithHighestWeightConsideringDefault(final String controllerId, List<Action> findActiveActionsWithHighestWeightConsideringDefault(final String controllerId,
final int maxActionCount) { final int maxActionCount) {
if (!actionRepository.activeActionExistsForControllerId(controllerId)) {
return Collections.emptyList();
}
final List<Action> actions = new ArrayList<>(); final List<Action> actions = new ArrayList<>();
final PageRequest pageable = PageRequest.of(0, maxActionCount); actions.addAll(
actions.addAll(actionRepository actionRepository
.findByTargetControllerIdAndActiveIsTrueAndWeightIsNotNullOrderByWeightDescIdAsc(pageable, controllerId) .findAll(
ActionSpecifications.byTargetControllerIdAndActiveAndWeightIsNullFetchDS(controllerId, false),
PageRequest.of(
0, maxActionCount,
Sort.by(
Sort.Order.desc(JpaAction_.WEIGHT),
Sort.Order.asc(JpaAction_.ID))))
.getContent()); .getContent());
actions.addAll(actionRepository
.findByTargetControllerIdAndActiveIsTrueAndWeightIsNullOrderByIdAsc(pageable, controllerId) actions.addAll(
actionRepository
.findAll(
ActionSpecifications.byTargetControllerIdAndActiveAndWeightIsNullFetchDS(controllerId, true),
PageRequest.of(
0, maxActionCount,
Sort.by(
Sort.Order.asc(JpaAction_.ID))))
.getContent()); .getContent());
final Comparator<Action> actionImportance = Comparator.comparingInt(this::getWeightConsideringDefault) final Comparator<Action> actionImportance = Comparator.comparingInt(this::getWeightConsideringDefault)
.reversed().thenComparing(Action::getId); .reversed().thenComparing(Action::getId);
@@ -72,11 +86,8 @@ public class JpaActionManagement {
} }
protected List<JpaAction> findActiveActionsHavingStatus(final String controllerId, final Action.Status status) { protected List<JpaAction> findActiveActionsHavingStatus(final String controllerId, final Action.Status status) {
if (!actionRepository.activeActionExistsForControllerId(controllerId)) { return actionRepository.findAll(
return Collections.emptyList(); ActionSpecifications.byTargetControllerIdAndIsActiveAndStatus(controllerId, status));
}
return Collections
.unmodifiableList(actionRepository.findByTargetIdAndIsActiveAndActionStatus(controllerId, status));
} }
protected Action addActionStatus(final JpaActionStatusCreate statusCreate) { protected Action addActionStatus(final JpaActionStatusCreate statusCreate) {
@@ -100,7 +111,7 @@ public class JpaActionManagement {
* Status.FINISHED are allowed. In the case of a DOWNLOAD_ONLY action, we accept * Status.FINISHED are allowed. In the case of a DOWNLOAD_ONLY action, we accept
* status updates only once. * status updates only once.
*/ */
protected boolean isUpdatingActionStatusAllowed(final JpaAction action, final JpaActionStatus actionStatus) { private boolean isUpdatingActionStatusAllowed(final JpaAction action, final JpaActionStatus actionStatus) {
final boolean isIntermediateFeedback = (FINISHED != actionStatus.getStatus()) final boolean isIntermediateFeedback = (FINISHED != actionStatus.getStatus())
&& (Action.Status.ERROR != actionStatus.getStatus()); && (Action.Status.ERROR != actionStatus.getStatus());
@@ -113,7 +124,7 @@ public class JpaActionManagement {
return action.isActive() || isAllowedByRepositoryConfiguration || isAllowedForDownloadOnlyActions; return action.isActive() || isAllowedByRepositoryConfiguration || isAllowedForDownloadOnlyActions;
} }
protected int getWeightConsideringDefault(final Action action) { public int getWeightConsideringDefault(final Action action) {
return action.getWeight().orElse(repositoryProperties.getActionWeightIfAbsent()); return action.getWeight().orElse(repositoryProperties.getActionWeightIfAbsent());
} }
@@ -130,7 +141,7 @@ public class JpaActionManagement {
/** /**
* Sets {@link TargetUpdateStatus} based on given {@link ActionStatus}. * Sets {@link TargetUpdateStatus} based on given {@link ActionStatus}.
*/ */
protected Action handleAddUpdateActionStatus(final JpaActionStatus actionStatus, final JpaAction action) { private Action handleAddUpdateActionStatus(final JpaActionStatus actionStatus, final JpaAction action) {
// information status entry - check for a potential DOS attack // information status entry - check for a potential DOS attack
assertActionStatusQuota(action); assertActionStatusQuota(action);
assertActionStatusMessageQuota(actionStatus); assertActionStatusMessageQuota(actionStatus);

View File

@@ -7,7 +7,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.management;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@@ -19,6 +19,7 @@ import org.eclipse.hawkbit.artifact.repository.HashNotMatchException;
import org.eclipse.hawkbit.artifact.repository.model.AbstractDbArtifact; import org.eclipse.hawkbit.artifact.repository.model.AbstractDbArtifact;
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact; import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
import org.eclipse.hawkbit.artifact.repository.model.DbArtifactHash; import org.eclipse.hawkbit.artifact.repository.model.DbArtifactHash;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.repository.ArtifactEncryptionService; import org.eclipse.hawkbit.repository.ArtifactEncryptionService;
import org.eclipse.hawkbit.repository.ArtifactManagement; import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.QuotaManagement; import org.eclipse.hawkbit.repository.QuotaManagement;
@@ -26,12 +27,19 @@ import org.eclipse.hawkbit.repository.exception.ArtifactDeleteFailedException;
import org.eclipse.hawkbit.repository.exception.ArtifactUploadFailedException; import org.eclipse.hawkbit.repository.exception.ArtifactUploadFailedException;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException; import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
import org.eclipse.hawkbit.repository.exception.InvalidMD5HashException; import org.eclipse.hawkbit.repository.exception.InvalidMD5HashException;
import org.eclipse.hawkbit.repository.exception.InvalidSHA1HashException; import org.eclipse.hawkbit.repository.exception.InvalidSHA1HashException;
import org.eclipse.hawkbit.repository.exception.InvalidSHA256HashException; import org.eclipse.hawkbit.repository.exception.InvalidSHA256HashException;
import org.eclipse.hawkbit.repository.jpa.EncryptionAwareDbArtifact;
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants; import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.model.JpaArtifact; import org.eclipse.hawkbit.repository.jpa.model.JpaArtifact;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule; import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.jpa.repository.LocalArtifactRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleRepository;
import org.eclipse.hawkbit.repository.jpa.specifications.ArtifactSpecifications;
import org.eclipse.hawkbit.repository.jpa.utils.FileSizeAndStorageQuotaCheckingInputStream; import org.eclipse.hawkbit.repository.jpa.utils.FileSizeAndStorageQuotaCheckingInputStream;
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper; import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
import org.eclipse.hawkbit.repository.model.Artifact; import org.eclipse.hawkbit.repository.model.Artifact;
@@ -45,9 +53,14 @@ import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.retry.annotation.Backoff; import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable; import org.springframework.retry.annotation.Retryable;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import javax.persistence.EntityManager;
/** /**
* JPA based {@link ArtifactManagement} implementation. * JPA based {@link ArtifactManagement} implementation.
* *
@@ -58,6 +71,8 @@ public class JpaArtifactManagement implements ArtifactManagement {
private static final Logger LOG = LoggerFactory.getLogger(JpaArtifactManagement.class); private static final Logger LOG = LoggerFactory.getLogger(JpaArtifactManagement.class);
private final EntityManager entityManager;
private final LocalArtifactRepository localArtifactRepository; private final LocalArtifactRepository localArtifactRepository;
private final SoftwareModuleRepository softwareModuleRepository; private final SoftwareModuleRepository softwareModuleRepository;
@@ -68,9 +83,11 @@ public class JpaArtifactManagement implements ArtifactManagement {
private final QuotaManagement quotaManagement; private final QuotaManagement quotaManagement;
JpaArtifactManagement(final LocalArtifactRepository localArtifactRepository, public JpaArtifactManagement(final EntityManager entityManager,
final LocalArtifactRepository localArtifactRepository,
final SoftwareModuleRepository softwareModuleRepository, final ArtifactRepository artifactRepository, final SoftwareModuleRepository softwareModuleRepository, final ArtifactRepository artifactRepository,
final QuotaManagement quotaManagement, final TenantAware tenantAware) { final QuotaManagement quotaManagement, final TenantAware tenantAware) {
this.entityManager = entityManager;
this.localArtifactRepository = localArtifactRepository; this.localArtifactRepository = localArtifactRepository;
this.softwareModuleRepository = softwareModuleRepository; this.softwareModuleRepository = softwareModuleRepository;
this.artifactRepository = artifactRepository; this.artifactRepository = artifactRepository;
@@ -78,37 +95,39 @@ public class JpaArtifactManagement implements ArtifactManagement {
this.tenantAware = tenantAware; this.tenantAware = tenantAware;
} }
private static Artifact checkForExistingArtifact(final String filename, final boolean overrideExisting,
final SoftwareModule softwareModule) {
final Optional<Artifact> artifact = softwareModule.getArtifactByFilename(filename);
if (artifact.isPresent()) {
if (overrideExisting) {
LOG.debug("overriding existing artifact with new filename {}", filename);
return artifact.get();
} else {
throw new EntityAlreadyExistsException("File with that name already exists in the Software Module");
}
}
return null;
}
@Override @Override
@Transactional @Transactional
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Artifact create(final ArtifactUpload artifactUpload) { public Artifact create(final ArtifactUpload artifactUpload) {
final String filename = artifactUpload.getFilename();
final long moduleId = artifactUpload.getModuleId(); final long moduleId = artifactUpload.getModuleId();
final SoftwareModule softwareModule = getModuleAndThrowExceptionIfThatFails(moduleId);
final Artifact existing = checkForExistingArtifact(filename, artifactUpload.overrideExisting(), softwareModule);
assertArtifactQuota(moduleId, 1); assertArtifactQuota(moduleId, 1);
final JpaSoftwareModule softwareModule =
softwareModuleRepository
.findById(moduleId)
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, moduleId));
final String filename = artifactUpload.getFilename();
final Artifact existing = softwareModule.getArtifactByFilename(filename).orElse(null);
if (existing != null) {
if (artifactUpload.overrideExisting()) {
LOG.debug("overriding existing artifact with new filename {}", filename);
} else {
throw new EntityAlreadyExistsException("File with that name already exists in the Software Module");
}
}
// touch it to update the lock revision because we are modifying the
// DS indirectly, it will, also check UPDATE access
JpaManagementHelper.touch(entityManager, softwareModuleRepository, softwareModule);
final AbstractDbArtifact artifact = storeArtifact(artifactUpload, softwareModule.isEncrypted()); final AbstractDbArtifact artifact = storeArtifact(artifactUpload, softwareModule.isEncrypted());
try {
return storeArtifactMetadata(softwareModule, filename, artifact, existing); return storeArtifactMetadata(softwareModule, filename, artifact, existing);
} catch (final Exception e) {
artifactRepository.deleteBySha1(tenantAware.getCurrentTenant(), artifact.getHashes().getSha1());
throw e;
}
} }
private AbstractDbArtifact storeArtifact(final ArtifactUpload artifactUpload, final boolean isSmEncrypted) { private AbstractDbArtifact storeArtifact(final ArtifactUpload artifactUpload, final boolean isSmEncrypted) {
@@ -142,64 +161,86 @@ public class JpaArtifactManagement implements ArtifactManagement {
return ArtifactEncryptionService.getInstance().encryptSoftwareModuleArtifact(smId, stream); return ArtifactEncryptionService.getInstance().encryptSoftwareModuleArtifact(smId, stream);
} }
private void assertArtifactQuota(final long id, final int requested) { private void assertArtifactQuota(final long moduleId, final int requested) {
QuotaHelper.assertAssignmentQuota(id, requested, quotaManagement.getMaxArtifactsPerSoftwareModule(), QuotaHelper.assertAssignmentQuota(
Artifact.class, SoftwareModule.class, localArtifactRepository::countBySoftwareModuleId); moduleId, requested, quotaManagement.getMaxArtifactsPerSoftwareModule(),
Artifact.class, SoftwareModule.class,
// get all artifacts without user context
softwareModuleId -> localArtifactRepository
.count(null, ArtifactSpecifications.bySoftwareModuleId(softwareModuleId)));
} }
private InputStream wrapInQuotaStream(final InputStream in) { private InputStream wrapInQuotaStream(final InputStream in) {
final long maxArtifactSize = quotaManagement.getMaxArtifactSize(); final long maxArtifactSize = quotaManagement.getMaxArtifactSize();
final long currentlyUsed = localArtifactRepository.getSumOfUndeletedArtifactSize().orElse(0L); final long currentlyUsed = localArtifactRepository.sumOfNonDeletedArtifactSize().orElse(0L);
final long maxArtifactSizeTotal = quotaManagement.getMaxArtifactStorage(); final long maxArtifactSizeTotal = quotaManagement.getMaxArtifactStorage();
return new FileSizeAndStorageQuotaCheckingInputStream(in, maxArtifactSize, return new FileSizeAndStorageQuotaCheckingInputStream(in, maxArtifactSize,
maxArtifactSizeTotal - currentlyUsed); maxArtifactSizeTotal - currentlyUsed);
} }
@Override /**
@Transactional * Garbage collects artifact binaries if only referenced by given
@Retryable(include = { * {@link SoftwareModule#getId()} or {@link SoftwareModule}'s that are
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) * marked as deleted.
public boolean clearArtifactBinary(final String sha1Hash, final long moduleId) { * <p/>
final long count = localArtifactRepository.countBySha1HashAndTenantAndSoftwareModuleDeletedIsFalse(sha1Hash, * Software module related UPDATE permission shall be checked by the callers!
*
* @param sha1Hash no longer needed
* @param softwareModuleId the garbage collection call is made for
*/
@PreAuthorize(SpPermission.SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
void clearArtifactBinary(final String sha1Hash) {
// countBySha1HashAndTenantAndSoftwareModuleDeletedIsFalse will skip ACM checks and
// will return total count as it should be
final long count = localArtifactRepository.countBySha1HashAndTenantAndSoftwareModuleDeletedIsFalse(
sha1Hash,
tenantAware.getCurrentTenant()); tenantAware.getCurrentTenant());
if (count > 1) { if (count <= 1) { // 1 artifact is the one being deleted!
// there are still other artifacts that need the binary // removes the real artifact ONLY AFTER the delete of artifact or software module
return false; // in local history has passed successfully (caller has permission and no errors)
} TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCommit() {
try { try {
LOG.debug("deleting artifact from repository {}", sha1Hash); LOG.debug("deleting artifact from repository {}", sha1Hash);
artifactRepository.deleteBySha1(tenantAware.getCurrentTenant(), sha1Hash); artifactRepository.deleteBySha1(tenantAware.getCurrentTenant(), sha1Hash);
return true;
} catch (final ArtifactStoreException e) { } catch (final ArtifactStoreException e) {
throw new ArtifactDeleteFailedException(e); throw new ArtifactDeleteFailedException(e);
} }
} }
});
} // else there are still other artifacts that need the binary
}
@Override @Override
@Transactional @Transactional
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void delete(final long id) { public void delete(final long id) {
final JpaArtifact existing = (JpaArtifact) get(id) final JpaArtifact toDelete = (JpaArtifact) get(id)
.orElseThrow(() -> new EntityNotFoundException(Artifact.class, id)); .orElseThrow(() -> new EntityNotFoundException(Artifact.class, id));
clearArtifactBinary(existing.getSha1Hash(), existing.getSoftwareModule().getId()); // clearArtifactBinary checks (unconditionally) software module UPDATE access
softwareModuleRepository.getAccessController().ifPresent(accessController ->
accessController.assertOperationAllowed(AccessController.Operation.UPDATE,
(JpaSoftwareModule) toDelete.getSoftwareModule()));
((JpaSoftwareModule) toDelete.getSoftwareModule()).removeArtifact(toDelete);
softwareModuleRepository.save((JpaSoftwareModule) toDelete.getSoftwareModule());
((JpaSoftwareModule) existing.getSoftwareModule()).removeArtifact(existing);
softwareModuleRepository.save((JpaSoftwareModule) existing.getSoftwareModule());
localArtifactRepository.deleteById(id); localArtifactRepository.deleteById(id);
clearArtifactBinary(toDelete.getSha1Hash());
} }
@Override @Override
public Optional<Artifact> get(final long id) { public Optional<Artifact> get(final long id) {
return Optional.ofNullable(localArtifactRepository.findById(id).orElse(null)); return localArtifactRepository.findById(id).map(Artifact.class::cast);
} }
@Override @Override
public Optional<Artifact> getByFilenameAndSoftwareModule(final String filename, final long softwareModuleId) { public Optional<Artifact> getByFilenameAndSoftwareModule(final String filename, final long softwareModuleId) {
throwExceptionIfSoftwareModuleDoesNotExist(softwareModuleId); assertSoftwareModuleExists(softwareModuleId);
return localArtifactRepository.findFirstByFilenameAndSoftwareModuleId(filename, softwareModuleId); return localArtifactRepository.findFirstByFilenameAndSoftwareModuleId(filename, softwareModuleId);
} }
@@ -215,30 +256,38 @@ public class JpaArtifactManagement implements ArtifactManagement {
} }
@Override @Override
public Page<Artifact> findBySoftwareModule(final Pageable pageReq, final long swId) { public Page<Artifact> findBySoftwareModule(final Pageable pageReq, final long softwareModuleId) {
throwExceptionIfSoftwareModuleDoesNotExist(swId); assertSoftwareModuleExists(softwareModuleId);
return localArtifactRepository.findBySoftwareModuleId(pageReq, swId); return localArtifactRepository
.findAll(ArtifactSpecifications.bySoftwareModuleId(softwareModuleId), pageReq)
.map(Artifact.class::cast);
} }
@Override @Override
public long countBySoftwareModule(final long swId) { public long countBySoftwareModule(final long softwareModuleId) {
throwExceptionIfSoftwareModuleDoesNotExist(swId); assertSoftwareModuleExists(softwareModuleId);
return localArtifactRepository.countBySoftwareModuleId(swId); return localArtifactRepository.count(ArtifactSpecifications.bySoftwareModuleId(softwareModuleId));
} }
private void throwExceptionIfSoftwareModuleDoesNotExist(final Long swId) { @Override
if (!softwareModuleRepository.existsById(swId)) { public long count() {
throw new EntityNotFoundException(SoftwareModule.class, swId); return localArtifactRepository.count();
}
} }
@Override @Override
public Optional<DbArtifact> loadArtifactBinary(final String sha1Hash, final long softwareModuleId, public Optional<DbArtifact> loadArtifactBinary(final String sha1Hash, final long softwareModuleId,
final boolean isEncrypted) { final boolean isEncrypted) {
assertSoftwareModuleExists(softwareModuleId);
final String tenant = tenantAware.getCurrentTenant(); final String tenant = tenantAware.getCurrentTenant();
if (artifactRepository.existsByTenantAndSha1(tenant, sha1Hash)) { if (artifactRepository.existsByTenantAndSha1(tenant, sha1Hash)) {
// assert artifact exists and belongs to the software module
findFirstBySHA1(sha1Hash)
// if not found no assertOperationAllowed shall fail
.orElseThrow(InsufficientPermissionException::new);
final DbArtifact dbArtifact = artifactRepository.getArtifactBySha1(tenant, sha1Hash); final DbArtifact dbArtifact = artifactRepository.getArtifactBySha1(tenant, sha1Hash);
return Optional.ofNullable( return Optional.ofNullable(
isEncrypted ? wrapInEncryptionAwareDbArtifact(softwareModuleId, dbArtifact) : dbArtifact); isEncrypted ? wrapInEncryptionAwareDbArtifact(softwareModuleId, dbArtifact) : dbArtifact);
@@ -247,39 +296,36 @@ public class JpaArtifactManagement implements ArtifactManagement {
return Optional.empty(); return Optional.empty();
} }
private final DbArtifact wrapInEncryptionAwareDbArtifact(final long smId, final DbArtifact dbArtifact) { private DbArtifact wrapInEncryptionAwareDbArtifact(final long softwareModuleId, final DbArtifact dbArtifact) {
if (dbArtifact == null) { if (dbArtifact == null) {
return null; return null;
} }
final ArtifactEncryptionService encryptionService = ArtifactEncryptionService.getInstance(); final ArtifactEncryptionService encryptionService = ArtifactEncryptionService.getInstance();
return new EncryptionAwareDbArtifact(dbArtifact, return new EncryptionAwareDbArtifact(dbArtifact,
stream -> encryptionService.decryptSoftwareModuleArtifact(smId, stream), stream -> encryptionService.decryptSoftwareModuleArtifact(softwareModuleId, stream),
encryptionService.encryptionSizeOverhead()); encryptionService.encryptionSizeOverhead());
} }
private Artifact storeArtifactMetadata(final SoftwareModule softwareModule, final String providedFilename, private Artifact storeArtifactMetadata(final SoftwareModule softwareModule, final String providedFilename,
final AbstractDbArtifact result, final Artifact existing) { final AbstractDbArtifact result, final Artifact existing) {
JpaArtifact artifact = (JpaArtifact) existing; final JpaArtifact artifact;
if (existing == null) { if (existing == null) {
artifact = new JpaArtifact(result.getHashes().getSha1(), providedFilename, softwareModule); artifact = new JpaArtifact(result.getHashes().getSha1(), providedFilename, softwareModule);
} else {
artifact = (JpaArtifact) existing;
artifact.setSha1Hash(result.getHashes().getSha1());
} }
artifact.setMd5Hash(result.getHashes().getMd5()); artifact.setMd5Hash(result.getHashes().getMd5());
artifact.setSha256Hash(result.getHashes().getSha256()); artifact.setSha256Hash(result.getHashes().getSha256());
artifact.setSha1Hash(result.getHashes().getSha1());
artifact.setSize(result.getSize()); artifact.setSize(result.getSize());
LOG.debug("storing new artifact into repository {}", artifact); LOG.debug("storing new artifact into repository {}", artifact);
return localArtifactRepository.save(artifact); return localArtifactRepository.save(AccessController.Operation.CREATE, artifact);
} }
@Override private void assertSoftwareModuleExists(final long softwareModuleId) {
public long count() { if (!softwareModuleRepository.existsById(softwareModuleId)) {
return localArtifactRepository.count(); throw new EntityNotFoundException(SoftwareModule.class, softwareModuleId);
} }
private SoftwareModule getModuleAndThrowExceptionIfThatFails(final Long moduleId) {
return softwareModuleRepository.findById(moduleId)
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, moduleId));
} }
} }

View File

@@ -7,7 +7,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.management;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
@@ -31,6 +31,9 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus; import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
import org.eclipse.hawkbit.repository.jpa.model.JpaAutoConfirmationStatus; import org.eclipse.hawkbit.repository.jpa.model.JpaAutoConfirmationStatus;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget; import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
import org.eclipse.hawkbit.repository.jpa.repository.ActionStatusRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications; import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.Status; import org.eclipse.hawkbit.repository.model.Action.Status;
@@ -64,7 +67,7 @@ public class JpaConfirmationManagement extends JpaActionManagement implements Co
/** /**
* Constructor * Constructor
*/ */
protected JpaConfirmationManagement(final TargetRepository targetRepository, public JpaConfirmationManagement(final TargetRepository targetRepository,
final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository, final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository,
final RepositoryProperties repositoryProperties, final QuotaManagement quotaManagement, final RepositoryProperties repositoryProperties, final QuotaManagement quotaManagement,
final EntityFactory entityFactory) { final EntityFactory entityFactory) {
@@ -208,7 +211,7 @@ public class JpaConfirmationManagement extends JpaActionManagement implements Co
action.getId(), autoConfirmationStatus.getInitiator(), autoConfirmationStatus.getCreatedBy()); action.getId(), autoConfirmationStatus.getInitiator(), autoConfirmationStatus.getCreatedBy());
// do not make use of // do not make use of
// org.eclipse.hawkbit.repository.jpa.JpaActionManagement.handleAddUpdateActionStatus // org.eclipse.hawkbit.repository.jpa.management.JpaActionManagement.handleAddUpdateActionStatus
// to bypass the quota check. Otherwise the action will not be confirmed in case // to bypass the quota check. Otherwise the action will not be confirmed in case
// of exceeded action status quota. // of exceeded action status quota.
action.setStatus(Status.RUNNING); action.setStatus(Status.RUNNING);

View File

@@ -7,7 +7,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.management;
import static org.eclipse.hawkbit.repository.model.Action.Status.DOWNLOADED; import static org.eclipse.hawkbit.repository.model.Action.Status.DOWNLOADED;
import static org.eclipse.hawkbit.repository.model.Action.Status.FINISHED; import static org.eclipse.hawkbit.repository.model.Action.Status.FINISHED;
@@ -56,6 +56,7 @@ import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException; import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.InvalidTargetAttributeException; import org.eclipse.hawkbit.repository.exception.InvalidTargetAttributeException;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.eclipse.hawkbit.repository.jpa.builder.JpaActionStatusCreate; import org.eclipse.hawkbit.repository.jpa.builder.JpaActionStatusCreate;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants; import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor; import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
@@ -66,6 +67,11 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaAction_;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget; import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_; import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
import org.eclipse.hawkbit.repository.jpa.repository.ActionStatusRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleMetadataRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
import org.eclipse.hawkbit.repository.jpa.specifications.ActionSpecifications; import org.eclipse.hawkbit.repository.jpa.specifications.ActionSpecifications;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications; import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications;
import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper; import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper;
@@ -302,13 +308,20 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
throwExceptionIfTargetDoesNotExist(controllerId); throwExceptionIfTargetDoesNotExist(controllerId);
throwExceptionIfSoftwareModuleDoesNotExist(moduleId); throwExceptionIfSoftwareModuleDoesNotExist(moduleId);
final List<Action> action = actionRepository.findActionByTargetAndSoftwareModule(controllerId, moduleId); // TODO AC - REVIEW
// it used to perform 3-table join query
if (action.isEmpty() || action.get(0).isCancelingOrCanceled()) { // @Query("Select a from JpaAction a join a.distributionSet ds join ds.modules modul where a.target.controllerId = :target and modul.id = :module order by a.id desc")
return Optional.empty(); // final List<Action> actions = actionRepository.findActionByTargetAndSoftwareModule(controllerId, moduleId);
} // TODO AC - we could fetch distribution sets in order to skip calls to serarch for modules
return actionRepository
return Optional.ofNullable(action.get(0)); .findAll(ActionSpecifications.byTargetControllerIdAndActive(controllerId, true))
.stream()
.filter(action -> !action.isCancelingOrCanceled())
.filter(action -> action.getDistributionSet().getModules()
.stream()
.anyMatch(module -> module.getId() == moduleId))
.map(Action.class::cast)
.findFirst();
} }
private void throwExceptionIfTargetDoesNotExist(final String controllerId) { private void throwExceptionIfTargetDoesNotExist(final String controllerId) {
@@ -358,12 +371,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
@Override @Override
public Optional<Action> findActionWithDetails(final long actionId) { public Optional<Action> findActionWithDetails(final long actionId) {
return actionRepository.getActionById(actionId); return actionRepository.findWithDetailsById(actionId);
}
@Override
public List<Action> getActiveActionsByExternalRef(@NotNull final List<String> externalRefs) {
return actionRepository.findByExternalRefInAndActive(externalRefs, true);
} }
@Override @Override
@@ -1006,6 +1014,15 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
@Override @Override
public void updateActionExternalRef(final long actionId, @NotEmpty final String externalRef) { public void updateActionExternalRef(final long actionId, @NotEmpty final String externalRef) {
// if access control for target repository is present check that caller has
// UPDATE access to the target of the action
targetRepository.getAccessController().ifPresent(
accessController -> accessController.assertOperationAllowed(
AccessController.Operation.UPDATE,
(JpaTarget) actionRepository
.findById(actionId)
.orElseThrow(() -> new EntityNotFoundException(Action.class, actionId))
.getTarget()));
actionRepository.updateExternalRef(actionId, externalRef); actionRepository.updateExternalRef(actionId, externalRef);
} }

View File

@@ -7,7 +7,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.management;
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED; import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED;
@@ -49,17 +49,23 @@ import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.ForceQuitActionNotAllowedException; import org.eclipse.hawkbit.repository.exception.ForceQuitActionNotAllowedException;
import org.eclipse.hawkbit.repository.exception.IncompatibleTargetTypeException; import org.eclipse.hawkbit.repository.exception.IncompatibleTargetTypeException;
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException; import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
import org.eclipse.hawkbit.repository.exception.MultiAssignmentIsNotEnabledException; import org.eclipse.hawkbit.repository.exception.MultiAssignmentIsNotEnabledException;
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants; import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor; import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction; import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus; import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus_; import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus_;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction_;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget; import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_; import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
import org.eclipse.hawkbit.repository.jpa.repository.ActionStatusRepository;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility; import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.jpa.specifications.ActionSpecifications;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications; import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications;
import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper; import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper;
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper; import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
@@ -151,7 +157,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
private final Database database; private final Database database;
private final RetryTemplate retryTemplate; private final RetryTemplate retryTemplate;
protected JpaDeploymentManagement(final EntityManager entityManager, final ActionRepository actionRepository, public JpaDeploymentManagement(final EntityManager entityManager, final ActionRepository actionRepository,
final DistributionSetManagement distributionSetManagement, final DistributionSetManagement distributionSetManagement,
final DistributionSetRepository distributionSetRepository, final TargetRepository targetRepository, final DistributionSetRepository distributionSetRepository, final TargetRepository targetRepository,
final ActionStatusRepository actionStatusRepository, final AuditorAware<String> auditorProvider, final ActionStatusRepository actionStatusRepository, final AuditorAware<String> auditorProvider,
@@ -185,13 +191,12 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
@Transactional(isolation = Isolation.READ_COMMITTED) @Transactional(isolation = Isolation.READ_COMMITTED)
public List<DistributionSetAssignmentResult> offlineAssignedDistributionSets( public List<DistributionSetAssignmentResult> offlineAssignedDistributionSets(
final Collection<Entry<String, Long>> assignments) { final Collection<Entry<String, Long>> assignments) {
final Collection<Entry<String, Long>> distinctAssignments = assignments.stream().distinct() final Collection<Entry<String, Long>> distinctAssignments = assignments.stream().distinct().toList();
.collect(Collectors.toList());
enforceMaxAssignmentsPerRequest(distinctAssignments.size()); enforceMaxAssignmentsPerRequest(distinctAssignments.size());
final List<DeploymentRequest> deploymentRequests = distinctAssignments.stream() final List<DeploymentRequest> deploymentRequests = distinctAssignments.stream()
.map(entry -> DeploymentManagement.deploymentRequest(entry.getKey(), entry.getValue()).build()) .map(entry -> DeploymentManagement.deploymentRequest(entry.getKey(), entry.getValue()).build())
.collect(Collectors.toList()); .toList();
return assignDistributionSets(tenantAware.getCurrentUsername(), deploymentRequests, null, return assignDistributionSets(tenantAware.getCurrentUsername(), deploymentRequests, null,
offlineDsAssignmentStrategy); offlineDsAssignmentStrategy);
@@ -216,36 +221,56 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
private List<DistributionSetAssignmentResult> assignDistributionSets(final String initiatedBy, private List<DistributionSetAssignmentResult> assignDistributionSets(final String initiatedBy,
final List<DeploymentRequest> deploymentRequests, final String actionMessage, final List<DeploymentRequest> deploymentRequests, final String actionMessage,
final AbstractDsAssignmentStrategy strategy) { final AbstractDsAssignmentStrategy strategy) {
final List<DeploymentRequest> validatedRequests = validateRequestForAssignments(deploymentRequests); final List<DeploymentRequest> validatedRequests = validateAndFilterRequestForAssignments(deploymentRequests);
final Map<Long, List<TargetWithActionType>> assignmentsByDsIds = convertRequest(validatedRequests); final Map<Long, List<TargetWithActionType>> assignmentsByDsIds = convertRequest(validatedRequests);
final List<DistributionSetAssignmentResult> results = assignmentsByDsIds.entrySet().stream() final List<DistributionSetAssignmentResult> results = assignmentsByDsIds.entrySet().stream()
.map(entry -> assignDistributionSetToTargetsWithRetry(initiatedBy, entry.getKey(), entry.getValue(), .map(entry -> assignDistributionSetToTargetsWithRetry(initiatedBy, entry.getKey(), entry.getValue(),
actionMessage, strategy)) actionMessage, strategy))
.collect(Collectors.toList()); .toList();
strategy.sendDeploymentEvents(results); strategy.sendDeploymentEvents(results);
return results; return results;
} }
private List<DeploymentRequest> validateRequestForAssignments(List<DeploymentRequest> deploymentRequests) { private List<DeploymentRequest> validateAndFilterRequestForAssignments(List<DeploymentRequest> deploymentRequests) {
if (!isMultiAssignmentsEnabled()) { if (deploymentRequests.isEmpty()) {
deploymentRequests = deploymentRequests.stream().distinct().collect(Collectors.toList());
checkIfRequiresMultiAssignment(deploymentRequests);
}
checkForTargetTypeCompatibility(deploymentRequests);
checkQuotaForAssignment(deploymentRequests);
return deploymentRequests; return deploymentRequests;
} }
deploymentRequests = deploymentRequests.stream().distinct().toList();
checkForMultiAssignment(deploymentRequests);
checkQuotaForAssignment(deploymentRequests);
// validates READ access to deployment sets, throws exception if deployment set is not accessible
checkForTargetTypeCompatibility(deploymentRequests);
// filters only targets that are updatable
// TODO - should assignments that contain non-existing/allowed devices be allowed anyway?
return filterByTargetUpdatable(deploymentRequests);
}
private void checkForMultiAssignment(final Collection<DeploymentRequest> deploymentRequests) {
if (!isMultiAssignmentsEnabled()) {
final long distinctTargetsInRequest = deploymentRequests.stream()
.map(request -> request.getTargetWithActionType().getControllerId()).distinct().count();
if (distinctTargetsInRequest < deploymentRequests.size()) {
throw new MultiAssignmentIsNotEnabledException();
}
}
}
private void checkQuotaForAssignment(final Collection<DeploymentRequest> deploymentRequests) {
enforceMaxAssignmentsPerRequest(deploymentRequests.size());
enforceMaxActionsPerTarget(deploymentRequests);
}
private void checkForTargetTypeCompatibility(final List<DeploymentRequest> deploymentRequests) { private void checkForTargetTypeCompatibility(final List<DeploymentRequest> deploymentRequests) {
final List<String> controllerIds = deploymentRequests.stream().map(DeploymentRequest::getControllerId) final List<String> controllerIds = deploymentRequests.stream().map(DeploymentRequest::getControllerId)
.distinct().collect(Collectors.toList()); .distinct().toList();
final List<Long> distSetIds = deploymentRequests.stream().map(DeploymentRequest::getDistributionSetId) final List<Long> distSetIds = deploymentRequests.stream().map(DeploymentRequest::getDistributionSetId)
.distinct().collect(Collectors.toList()); .distinct().toList();
if (controllerIds.size() > 1 && distSetIds.size() > 1) { if (controllerIds.size() > 1 && distSetIds.size() > 1) {
throw new IllegalStateException( throw new IllegalStateException(
"Assigning multiple Targets to multiple Distribution Sets simultaneously is not allowed!"); "Assigning multiple Distribution Sets to multiple Targets simultaneously is not allowed!");
} }
if (distSetIds.size() == 1) { if (distSetIds.size() == 1) {
@@ -287,20 +312,30 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
} }
} }
private List<DeploymentRequest> filterByTargetUpdatable(final List<DeploymentRequest> deploymentRequests) {
final List<String> controllerIds =
deploymentRequests.stream()
.map(DeploymentRequest::getControllerId)
.distinct()
.toList();
final List<String> found = targetRepository.findAll(
AccessController.Operation.UPDATE,
TargetSpecifications.hasControllerIdIn(controllerIds)
).stream().map(JpaTarget::getControllerId).toList();
if (found.size() != controllerIds.size()) {
return deploymentRequests.stream()
.filter(deploymentRequest -> found.contains(deploymentRequest.getControllerId())).toList();
}
return deploymentRequests;
}
private static Map<Long, List<TargetWithActionType>> convertRequest( private static Map<Long, List<TargetWithActionType>> convertRequest(
final Collection<DeploymentRequest> deploymentRequests) { final Collection<DeploymentRequest> deploymentRequests) {
return deploymentRequests.stream().collect(Collectors.groupingBy(DeploymentRequest::getDistributionSetId, return deploymentRequests.stream().collect(Collectors.groupingBy(DeploymentRequest::getDistributionSetId,
Collectors.mapping(DeploymentRequest::getTargetWithActionType, Collectors.toList()))); Collectors.mapping(DeploymentRequest::getTargetWithActionType, Collectors.toList())));
} }
private static void checkIfRequiresMultiAssignment(final Collection<DeploymentRequest> deploymentRequests) {
final long distinctTargetsInRequest = deploymentRequests.stream()
.map(request -> request.getTargetWithActionType().getControllerId()).distinct().count();
if (distinctTargetsInRequest < deploymentRequests.size()) {
throw new MultiAssignmentIsNotEnabledException();
}
}
private DistributionSetAssignmentResult assignDistributionSetToTargetsWithRetry(final String initiatedBy, private DistributionSetAssignmentResult assignDistributionSetToTargetsWithRetry(final String initiatedBy,
final Long dsID, final Collection<TargetWithActionType> targetsWithActionType, final String actionMessage, final Long dsID, final Collection<TargetWithActionType> targetsWithActionType, final String actionMessage,
final AbstractDsAssignmentStrategy assignmentStrategy) { final AbstractDsAssignmentStrategy assignmentStrategy) {
@@ -310,17 +345,16 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
} }
/** /**
* method assigns the {@link DistributionSet} to all {@link Target}s by * method assigns the {@link DistributionSet} to all {@link Target}s by their
* their IDs with a specific {@link ActionType} and {@code forcetime}. * IDs with a specific {@link ActionType} and {@code forcetime}.
* * <p/>
*
* In case the update was executed offline (i.e. not managed by hawkBit) the * In case the update was executed offline (i.e. not managed by hawkBit) the
* handling differs my means that:<br/> * handling differs my means that:<br/>
* A. it ignores targets completely that are in * A. it ignores targets completely that are in
* {@link TargetUpdateStatus#PENDING}.<br/> * {@link TargetUpdateStatus#PENDING}.<br/>
* B. it created completed actions.<br/> * B. it created completed actions.<br/>
* C. sets both installed and assigned DS on the target and switches the * C. sets both installed and assigned DS on the target and switches the status
* status to {@link TargetUpdateStatus#IN_SYNC} <br/> * to {@link TargetUpdateStatus#IN_SYNC} <br/>
* D. does not send a {@link TargetAssignDistributionSetEvent}.<br/> * D. does not send a {@link TargetAssignDistributionSetEvent}.<br/>
* *
* @param initiatedBy * @param initiatedBy
@@ -346,11 +380,13 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
final JpaDistributionSet distributionSetEntity = (JpaDistributionSet) distributionSetManagement final JpaDistributionSet distributionSetEntity = (JpaDistributionSet) distributionSetManagement
.getValidAndComplete(dsID); .getValidAndComplete(dsID);
final List<String> providedTargetIds = targetsWithActionType.stream().map(TargetWithActionType::getControllerId) final List<String> providedTargetIds = targetsWithActionType.stream().map(TargetWithActionType::getControllerId)
.distinct().collect(Collectors.toList()); .distinct().toList();
final List<String> existingTargetIds = Lists.partition(providedTargetIds, Constants.MAX_ENTRIES_IN_STATEMENT) final List<String> existingTargetIds = Lists.partition(providedTargetIds, Constants.MAX_ENTRIES_IN_STATEMENT)
.stream().map(ids -> targetRepository.findAll(TargetSpecifications.hasControllerIdIn(ids))) .stream()
.flatMap(List::stream).map(JpaTarget::getControllerId).collect(Collectors.toList()); .map(ids -> targetRepository.findAll(
AccessController.Operation.UPDATE, TargetSpecifications.hasControllerIdIn(ids)))
.flatMap(List::stream).map(JpaTarget::getControllerId).toList();
final List<JpaTarget> targetEntities = assignmentStrategy.findTargetsForAssignment(existingTargetIds, final List<JpaTarget> targetEntities = assignmentStrategy.findTargetsForAssignment(existingTargetIds,
distributionSetEntity.getId()); distributionSetEntity.getId());
@@ -360,7 +396,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
} }
final List<TargetWithActionType> existingTargetsWithActionType = targetsWithActionType.stream() final List<TargetWithActionType> existingTargetsWithActionType = targetsWithActionType.stream()
.filter(target -> existingTargetIds.contains(target.getControllerId())).collect(Collectors.toList()); .filter(target -> existingTargetIds.contains(target.getControllerId())).toList();
final List<JpaAction> assignedActions = doAssignDistributionSetToTargets(initiatedBy, final List<JpaAction> assignedActions = doAssignDistributionSetToTargets(initiatedBy,
existingTargetsWithActionType, actionMessage, assignmentStrategy, distributionSetEntity, existingTargetsWithActionType, actionMessage, assignmentStrategy, distributionSetEntity,
@@ -405,9 +441,9 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
} }
/** /**
* split tIDs length into max entries in-statement because many database * split tIDs length into max entries in-statement because many database have
* have constraint of max entries in in-statements e.g. Oracle with maximum * constraint of max entries in in-statements e.g. Oracle with maximum 1000
* 1000 elements, so we need to split the entries here and execute multiple * elements, so we need to split the entries here and execute multiple
* statements * statements
*/ */
private static List<List<Long>> getTargetEntitiesAsChunks(final List<JpaTarget> targetEntities) { private static List<List<Long>> getTargetEntitiesAsChunks(final List<JpaTarget> targetEntities) {
@@ -422,13 +458,6 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
return new DistributionSetAssignmentResult(distributionSet, alreadyAssignedTargetsCount, assignedActions); return new DistributionSetAssignmentResult(distributionSet, alreadyAssignedTargetsCount, assignedActions);
} }
private void checkQuotaForAssignment(final Collection<DeploymentRequest> deploymentRequests) {
if (!deploymentRequests.isEmpty()) {
enforceMaxAssignmentsPerRequest(deploymentRequests.size());
enforceMaxActionsPerTarget(deploymentRequests);
}
}
private void enforceMaxAssignmentsPerRequest(final int requestedActions) { private void enforceMaxAssignmentsPerRequest(final int requestedActions) {
QuotaHelper.assertAssignmentRequestSizeQuota(requestedActions, QuotaHelper.assertAssignmentRequestSizeQuota(requestedActions,
quotaManagement.getMaxTargetDistributionSetAssignmentsPerManualAssignment()); quotaManagement.getMaxTargetDistributionSetAssignmentsPerManualAssignment());
@@ -437,11 +466,11 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
private void enforceMaxActionsPerTarget(final Collection<DeploymentRequest> deploymentRequests) { private void enforceMaxActionsPerTarget(final Collection<DeploymentRequest> deploymentRequests) {
final int quota = quotaManagement.getMaxActionsPerTarget(); final int quota = quotaManagement.getMaxActionsPerTarget();
final Map<String, Long> countOfTargtInRequest = deploymentRequests.stream() final Map<String, Long> countOfTargetInRequest = deploymentRequests.stream()
.map(DeploymentRequest::getControllerId) .map(DeploymentRequest::getControllerId)
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting())); .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
countOfTargtInRequest.forEach((controllerId, count) -> QuotaHelper.assertAssignmentQuota(controllerId, count, countOfTargetInRequest.forEach((controllerId, count) -> QuotaHelper.assertAssignmentQuota(controllerId, count,
quota, Action.class, Target.class, actionRepository::countByTargetControllerId)); quota, Action.class, Target.class, actionRepository::countByTargetControllerId));
} }
@@ -460,6 +489,11 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void cancelInactiveScheduledActionsForTargets(final List<Long> targetIds) { public void cancelInactiveScheduledActionsForTargets(final List<Long> targetIds) {
if (!isMultiAssignmentsEnabled()) { if (!isMultiAssignmentsEnabled()) {
targetRepository.getAccessController().ifPresent(v -> {
if (targetRepository.count(AccessController.Operation.UPDATE, TargetSpecifications.hasIdIn(targetIds)) != targetIds.size()) {
throw new EntityNotFoundException(Target.class, targetIds);
}
});
actionRepository.switchStatus(Status.CANCELED, targetIds, false, Status.SCHEDULED); actionRepository.switchStatus(Status.CANCELED, targetIds, false, Status.SCHEDULED);
} else { } else {
LOG.debug("The Multi Assignments feature is enabled: No need to cancel inactive scheduled actions."); LOG.debug("The Multi Assignments feature is enabled: No need to cancel inactive scheduled actions.");
@@ -494,7 +528,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
final JpaActionStatus actionStatus = assignmentStrategy.createActionStatus(action, actionMessage); final JpaActionStatus actionStatus = assignmentStrategy.createActionStatus(action, actionMessage);
verifyAndAddConfirmationStatus(action, actionStatus, entry.getKey().isConfirmationRequired()); verifyAndAddConfirmationStatus(action, actionStatus, entry.getKey().isConfirmationRequired());
return actionStatus; return actionStatus;
}).collect(Collectors.toList())); }).toList());
} }
private void setInitialActionStatusOfRolloutGroup(final List<JpaAction> actions) { private void setInitialActionStatusOfRolloutGroup(final List<JpaAction> actions) {
@@ -554,6 +588,8 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
throw new CancelActionNotAllowedException("Actions in canceling or canceled state cannot be canceled"); throw new CancelActionNotAllowedException("Actions in canceling or canceled state cannot be canceled");
} }
assertTargetUpdateAllowed(action);
if (action.isActive()) { if (action.isActive()) {
LOG.debug("action ({}) was still active. Change to {}.", action, Status.CANCELING); LOG.debug("action ({}) was still active. Change to {}.", action, Status.CANCELING);
action.setStatus(Status.CANCELING); action.setStatus(Status.CANCELING);
@@ -588,6 +624,8 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
throw new ForceQuitActionNotAllowedException(action.getId() + " is not active and cannot be force quit"); throw new ForceQuitActionNotAllowedException(action.getId() + " is not active and cannot be force quit");
} }
assertTargetUpdateAllowed(action);
LOG.warn("action ({}) was still active and has been force quite.", action); LOG.warn("action ({}) was still active and has been force quite.", action);
// document that the status has been retrieved // document that the status has been retrieved
@@ -738,93 +776,85 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
@Override @Override
public Optional<Action> findAction(final long actionId) { public Optional<Action> findAction(final long actionId) {
return actionRepository.findById(actionId).map(a -> a); return actionRepository
.findById(actionId)
.filter(action -> targetRepository.exists(TargetSpecifications.hasId(action.getTarget().getId())))
.map(JpaAction.class::cast);
} }
@Override @Override
public Optional<Action> findActionWithDetails(final long actionId) { public Optional<Action> findActionWithDetails(final long actionId) {
return actionRepository.getActionById(actionId); return actionRepository
.findWithDetailsById(actionId)
.filter(action -> targetRepository.exists(TargetSpecifications.hasId(action.getTarget().getId())));
} }
@Override @Override
public Slice<Action> findActionsByTarget(final String controllerId, final Pageable pageable) { public Slice<Action> findActionsByTarget(final String controllerId, final Pageable pageable) {
throwExceptionIfTargetDoesNotExist(controllerId); assertTargetReadAllowed(controllerId);
return actionRepository.findByTargetControllerId(pageable, controllerId); return actionRepository
.findAll(ActionSpecifications.byTargetControllerId(controllerId), pageable)
.map(Action.class::cast);
} }
@Override @Override
public Page<Action> findActionsByTarget(final String rsqlParam, final String controllerId, public Page<Action> findActionsByTarget(final String rsqlParam, final String controllerId,
final Pageable pageable) { final Pageable pageable) {
throwExceptionIfTargetDoesNotExist(controllerId); assertTargetReadAllowed(controllerId);
final List<Specification<JpaAction>> specList = Arrays.asList( final List<Specification<JpaAction>> specList = Arrays.asList(
RSQLUtility.buildRsqlSpecification(rsqlParam, ActionFields.class, virtualPropertyReplacer, database), RSQLUtility.buildRsqlSpecification(rsqlParam, ActionFields.class, virtualPropertyReplacer, database),
byControllerIdSpec(controllerId)); ActionSpecifications.byTargetControllerId(controllerId));
return JpaManagementHelper.findAllWithCountBySpec(actionRepository, pageable, specList); return JpaManagementHelper.findAllWithCountBySpec(actionRepository, pageable, specList);
} }
private Specification<JpaAction> byControllerIdSpec(final String controllerId) {
return (root, query, cb) -> cb.equal(root.get(JpaAction_.target).get(JpaTarget_.controllerId), controllerId);
}
@Override @Override
public Page<Action> findActiveActionsByTarget(final Pageable pageable, final String controllerId) { public Page<Action> findActiveActionsByTarget(final Pageable pageable, final String controllerId) {
throwExceptionIfTargetDoesNotExist(controllerId); assertTargetReadAllowed(controllerId);
return actionRepository.findByActiveAndTarget(pageable, controllerId, true); return actionRepository
.findAll(ActionSpecifications.byTargetControllerIdAndActive(controllerId, true), pageable)
.map(Action.class::cast);
} }
@Override @Override
public Page<Action> findInActiveActionsByTarget(final Pageable pageable, final String controllerId) { public Page<Action> findInActiveActionsByTarget(final Pageable pageable, final String controllerId) {
throwExceptionIfTargetDoesNotExist(controllerId); assertTargetReadAllowed(controllerId);
return actionRepository
return actionRepository.findByActiveAndTarget(pageable, controllerId, false); .findAll(ActionSpecifications.byTargetControllerIdAndActive(controllerId, false), pageable)
.map(Action.class::cast);
} }
@Override @Override
public List<Action> findActiveActionsWithHighestWeight(final String controllerId, final int maxActionCount) { public List<Action> findActiveActionsWithHighestWeight(final String controllerId, final int maxActionCount) {
assertTargetReadAllowed(controllerId);
return findActiveActionsWithHighestWeightConsideringDefault(controllerId, maxActionCount); return findActiveActionsWithHighestWeightConsideringDefault(controllerId, maxActionCount);
} }
@Override
public int getWeightConsideringDefault(final Action action) {
return super.getWeightConsideringDefault(action);
}
@Override @Override
public long countActionsByTarget(final String controllerId) { public long countActionsByTarget(final String controllerId) {
throwExceptionIfTargetDoesNotExist(controllerId); assertTargetReadAllowed(controllerId);
return actionRepository.countByTargetControllerId(controllerId); return actionRepository.countByTargetControllerId(controllerId);
} }
@Override @Override
public long countActionsByTarget(final String rsqlParam, final String controllerId) { public long countActionsByTarget(final String rsqlParam, final String controllerId) {
throwExceptionIfTargetDoesNotExist(controllerId); assertTargetReadAllowed(controllerId);
final List<Specification<JpaAction>> specList = Arrays.asList( final List<Specification<JpaAction>> specList = Arrays.asList(
RSQLUtility.buildRsqlSpecification(rsqlParam, ActionFields.class, virtualPropertyReplacer, database), RSQLUtility.buildRsqlSpecification(rsqlParam, ActionFields.class, virtualPropertyReplacer, database),
byControllerIdSpec(controllerId)); ActionSpecifications.byTargetControllerId(controllerId));
return JpaManagementHelper.countBySpec(actionRepository, specList); return JpaManagementHelper.countBySpec(actionRepository, specList);
} }
private void throwExceptionIfTargetDoesNotExist(final String controllerId) {
if (!targetRepository.exists(TargetSpecifications.hasControllerId(controllerId))) {
throw new EntityNotFoundException(Target.class, controllerId);
}
}
private void throwExceptionIfDistributionSetDoesNotExist(final Long dsId) {
if (!distributionSetRepository.existsById(dsId)) {
throw new EntityNotFoundException(DistributionSet.class, dsId);
}
}
@Override @Override
@Transactional @Transactional
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Action forceTargetAction(final long actionId) { public Action forceTargetAction(final long actionId) {
final JpaAction action = actionRepository.findById(actionId) final JpaAction action = actionRepository.findById(actionId)
.map(this::assertTargetUpdateAllowed)
.orElseThrow(() -> new EntityNotFoundException(Action.class, actionId)); .orElseThrow(() -> new EntityNotFoundException(Action.class, actionId));
if (!action.isForcedOrTimeForced()) { if (!action.isForcedOrTimeForced()) {
@@ -836,24 +866,20 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
@Override @Override
public Page<ActionStatus> findActionStatusByAction(final Pageable pageReq, final long actionId) { public Page<ActionStatus> findActionStatusByAction(final Pageable pageReq, final long actionId) {
verifyActionExists(actionId); assertActionExistsAndAccessible(actionId);
return actionStatusRepository.findByActionId(pageReq, actionId); return actionStatusRepository.findByActionId(pageReq, actionId);
} }
private void verifyActionExists(final long actionId) {
if (!actionRepository.existsById(actionId)) {
throw new EntityNotFoundException(Action.class, actionId);
}
}
@Override @Override
public long countActionStatusByAction(final long actionId) { public long countActionStatusByAction(final long actionId) {
verifyActionExists(actionId); assertActionExistsAndAccessible(actionId);
return actionStatusRepository.countByActionId(actionId); return actionStatusRepository.countByActionId(actionId);
} }
// action is already got and there are checked read permissions - do not check permissions
// and UI which is to be removed
@Override @Override
public Page<String> findMessagesByActionStatusId(final Pageable pageable, final long actionStatusId) { public Page<String> findMessagesByActionStatusId(final Pageable pageable, final long actionStatusId) {
final CriteriaBuilder cb = entityManager.getCriteriaBuilder(); final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
@@ -883,16 +909,6 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
return entityManager.createQuery(countMsgQuery).getSingleResult(); return entityManager.createQuery(countMsgQuery).getSingleResult();
} }
@Override
public Page<ActionStatus> findActionStatusAll(final Pageable pageable) {
return JpaManagementHelper.findAllWithCountBySpec(actionStatusRepository, pageable, null);
}
@Override
public long countActionStatusAll() {
return actionStatusRepository.count();
}
@Override @Override
public long countActionsAll() { public long countActionsAll() {
return actionRepository.count(); return actionRepository.count();
@@ -900,49 +916,37 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
@Override @Override
public long countActions(final String rsqlParam) { public long countActions(final String rsqlParam) {
final List<Specification<JpaAction>> specList = Arrays.asList( final List<Specification<JpaAction>> specList = List.of(
RSQLUtility.buildRsqlSpecification(rsqlParam, ActionFields.class, virtualPropertyReplacer, database)); RSQLUtility.buildRsqlSpecification(rsqlParam, ActionFields.class, virtualPropertyReplacer, database));
return JpaManagementHelper.countBySpec(actionRepository, specList); return JpaManagementHelper.countBySpec(actionRepository, specList);
} }
@Override // TODO - return via Mgmt API all actions (event for targets the use has no access - check if should and could
public long countActionsByDistributionSetIdAndActiveIsTrue(final Long distributionSet) { // be limited
return actionRepository.countByDistributionSetIdAndActiveIsTrue(distributionSet);
}
@Override
public long countActionsByDistributionSetIdAndActiveIsTrueAndStatusIsNot(final Long distributionSet,
final Status status) {
return actionRepository.countByDistributionSetIdAndActiveIsTrueAndStatusIsNot(distributionSet, status);
}
@Override
public Slice<Action> findActionsByDistributionSet(final Pageable pageable, final long dsId) {
throwExceptionIfDistributionSetDoesNotExist(dsId);
return actionRepository.findByDistributionSetId(pageable, dsId);
}
@Override @Override
public Slice<Action> findActionsAll(final Pageable pageable) { public Slice<Action> findActionsAll(final Pageable pageable) {
return JpaManagementHelper.findAllWithoutCountBySpec(actionRepository, pageable, null); return JpaManagementHelper.findAllWithoutCountBySpec(actionRepository, pageable, null);
} }
// TODO - return via Mgmt API all actions (event for targets the use has no access - check if should and could
// be limited
@Override @Override
public Slice<Action> findActions(final String rsqlParam, final Pageable pageable) { public Slice<Action> findActions(final String rsqlParam, final Pageable pageable) {
final List<Specification<JpaAction>> specList = Arrays.asList( final List<Specification<JpaAction>> specList = List.of(
RSQLUtility.buildRsqlSpecification(rsqlParam, ActionFields.class, virtualPropertyReplacer, database)); RSQLUtility.buildRsqlSpecification(rsqlParam, ActionFields.class, virtualPropertyReplacer, database));
return JpaManagementHelper.findAllWithoutCountBySpec(actionRepository, pageable, specList); return JpaManagementHelper.findAllWithoutCountBySpec(actionRepository, pageable, specList);
} }
@Override @Override
public Optional<DistributionSet> getAssignedDistributionSet(final String controllerId) { public Optional<DistributionSet> getAssignedDistributionSet(final String controllerId) {
throwExceptionIfTargetDoesNotExist(controllerId); // target access checked in assertTargetReadAllowed
assertTargetReadAllowed(controllerId);
return distributionSetRepository.findAssignedToTarget(controllerId); return distributionSetRepository.findAssignedToTarget(controllerId);
} }
@Override @Override
public Optional<DistributionSet> getInstalledDistributionSet(final String controllerId) { public Optional<DistributionSet> getInstalledDistributionSet(final String controllerId) {
throwExceptionIfTargetDoesNotExist(controllerId); assertTargetReadAllowed(controllerId);
return distributionSetRepository.findInstalledAtTarget(controllerId); return distributionSetRepository.findInstalledAtTarget(controllerId);
} }
@@ -953,10 +957,10 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
return 0; return 0;
} }
/* /*
* We use a native query here because Spring JPA does not support to * We use a native query here because Spring JPA does not support to specify a
* specify a LIMIT clause on a DELETE statement. However, for this * LIMIT clause on a DELETE statement. However, for this specific use case
* specific use case (action cleanup), we must specify a row limit to * (action cleanup), we must specify a row limit to reduce the overall load on
* reduce the overall load on the database. * the database.
*/ */
final int statusCount = status.size(); final int statusCount = status.size();
@@ -976,9 +980,11 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
} }
@Override @Override
public boolean hasPendingCancellations(final String controllerId) { public boolean hasPendingCancellations(final Long targetId) {
return actionRepository.existsByTargetControllerIdAndStatusAndActiveIsTrue(controllerId, // target access checked in assertTargetReadAllowed
Action.Status.CANCELING); assertTargetReadAllowed(targetId);
return actionRepository.exists(
ActionSpecifications.byTargetIdAndIsActiveAndStatus(targetId, Action.Status.CANCELING));
} }
private static String getQueryForDeleteActionsByStatusAndLastModifiedBeforeString(final Database database) { private static String getQueryForDeleteActionsByStatusAndLastModifiedBeforeString(final Database database) {
@@ -1033,18 +1039,59 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
@Override @Override
@Transactional @Transactional
public void cancelActionsForDistributionSet(final CancelationType cancelationType, final DistributionSet set) { public void cancelActionsForDistributionSet(
actionRepository.findByDistributionSetAndActiveIsTrueAndStatusIsNot(set, Status.CANCELING).forEach(action -> { final CancelationType cancelationType, final DistributionSet distributionSet) {
final JpaAction jpaAction = (JpaAction) action; actionRepository
cancelAction(jpaAction.getId()); .findAll(ActionSpecifications.byDistributionSetIdAndActiveAndStatusIsNot(distributionSet.getId(), Status.CANCELING))
LOG.debug("Action {} canceled", jpaAction.getId()); .forEach(action -> {
try {
assertTargetUpdateAllowed(action);
cancelAction(action.getId());
LOG.debug("Action {} canceled", action.getId());
} catch (final InsufficientPermissionException e) {
// no access - skip it
}
}); });
if (cancelationType == CancelationType.FORCE) { if (cancelationType == CancelationType.FORCE) {
actionRepository.findByDistributionSetAndActiveIsTrue(set).forEach(action -> { actionRepository
final JpaAction jpaAction = (JpaAction) action; .findAll(ActionSpecifications.byDistributionSetIdAndActive(distributionSet.getId()))
forceQuitAction(jpaAction.getId()); .forEach(action -> {
LOG.debug("Action {} force canceled", jpaAction.getId()); try {
assertTargetUpdateAllowed(action);
forceQuitAction(action.getId());
LOG.debug("Action {} force canceled", action.getId());
} catch (final InsufficientPermissionException e) {
// no access - skip it
}
}); });
} }
} }
private void assertTargetReadAllowed(final Long targetId) {
if (!targetRepository.existsById(targetId)) {
throw new EntityNotFoundException(Target.class, targetId);
}
}
private void assertTargetReadAllowed(final String controllerId) {
if (!targetRepository.exists(TargetSpecifications.hasControllerId(controllerId))) {
throw new EntityNotFoundException(Target.class, controllerId);
}
}
private JpaAction assertTargetUpdateAllowed(final JpaAction action) {
if (!targetRepository.exists(TargetSpecifications.hasId(action.getTarget().getId()))) {
throw new EntityNotFoundException(Action.class, action);
}
return action;
}
private void assertActionExistsAndAccessible(final long actionId) {
if (actionRepository.findById(actionId).filter(action -> {
assertTargetReadAllowed(action.getTarget().getId());
return true;
}).isEmpty()) {
throw new EntityNotFoundException(Action.class, actionId);
}
}
} }

View File

@@ -7,7 +7,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.management;
import java.util.Collection; import java.util.Collection;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@@ -20,6 +20,7 @@ import org.eclipse.hawkbit.repository.RepositoryProperties;
import org.eclipse.hawkbit.repository.RolloutManagement; import org.eclipse.hawkbit.repository.RolloutManagement;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement; import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.exception.StopRolloutException; import org.eclipse.hawkbit.repository.exception.StopRolloutException;
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper; import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper;
import org.eclipse.hawkbit.repository.model.Action.Status; import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
@@ -44,20 +45,23 @@ public class JpaDistributionSetInvalidationManagement implements DistributionSet
private final RolloutManagement rolloutManagement; private final RolloutManagement rolloutManagement;
private final DeploymentManagement deploymentManagement; private final DeploymentManagement deploymentManagement;
private final TargetFilterQueryManagement targetFilterQueryManagement; private final TargetFilterQueryManagement targetFilterQueryManagement;
private final ActionRepository actionRepository;
private final PlatformTransactionManager txManager; private final PlatformTransactionManager txManager;
private final RepositoryProperties repositoryProperties; private final RepositoryProperties repositoryProperties;
private final TenantAware tenantAware; private final TenantAware tenantAware;
private final LockRegistry lockRegistry; private final LockRegistry lockRegistry;
protected JpaDistributionSetInvalidationManagement(final DistributionSetManagement distributionSetManagement, public JpaDistributionSetInvalidationManagement(final DistributionSetManagement distributionSetManagement,
final RolloutManagement rolloutManagement, final DeploymentManagement deploymentManagement, final RolloutManagement rolloutManagement, final DeploymentManagement deploymentManagement,
final TargetFilterQueryManagement targetFilterQueryManagement, final PlatformTransactionManager txManager, final TargetFilterQueryManagement targetFilterQueryManagement, final ActionRepository actionRepository,
final PlatformTransactionManager txManager,
final RepositoryProperties repositoryProperties, final TenantAware tenantAware, final RepositoryProperties repositoryProperties, final TenantAware tenantAware,
final LockRegistry lockRegistry) { final LockRegistry lockRegistry) {
this.distributionSetManagement = distributionSetManagement; this.distributionSetManagement = distributionSetManagement;
this.rolloutManagement = rolloutManagement; this.rolloutManagement = rolloutManagement;
this.deploymentManagement = deploymentManagement; this.deploymentManagement = deploymentManagement;
this.targetFilterQueryManagement = targetFilterQueryManagement; this.targetFilterQueryManagement = targetFilterQueryManagement;
this.actionRepository = actionRepository;
this.txManager = txManager; this.txManager = txManager;
this.repositoryProperties = repositoryProperties; this.repositoryProperties = repositoryProperties;
this.tenantAware = tenantAware; this.tenantAware = tenantAware;
@@ -155,12 +159,11 @@ public class JpaDistributionSetInvalidationManagement implements DistributionSet
} }
private long countActionsForForcedInvalidation(final Collection<Long> setIds) { private long countActionsForForcedInvalidation(final Collection<Long> setIds) {
return setIds.stream().mapToLong(deploymentManagement::countActionsByDistributionSetIdAndActiveIsTrue).sum(); return setIds.stream().mapToLong(actionRepository::countByDistributionSetIdAndActiveIsTrue).sum();
} }
private long countActionsForSoftInvalidation(final Collection<Long> setIds) { private long countActionsForSoftInvalidation(final Collection<Long> setIds) {
return setIds.stream().mapToLong(distributionSet -> deploymentManagement return setIds.stream().mapToLong(distributionSet -> actionRepository
.countActionsByDistributionSetIdAndActiveIsTrueAndStatusIsNot(distributionSet, Status.CANCELING)).sum(); .countByDistributionSetIdAndActiveIsTrueAndStatusIsNot(distributionSet, Status.CANCELING)).sum();
} }
} }

View File

@@ -7,13 +7,16 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.management;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.persistence.EntityManager; import javax.persistence.EntityManager;
@@ -35,7 +38,10 @@ import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException; import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException; import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
import org.eclipse.hawkbit.repository.exception.InvalidDistributionSetException; import org.eclipse.hawkbit.repository.exception.InvalidDistributionSetException;
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.eclipse.hawkbit.repository.jpa.builder.JpaDistributionSetCreate; import org.eclipse.hawkbit.repository.jpa.builder.JpaDistributionSetCreate;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants; import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor; import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
@@ -45,9 +51,16 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetMetadata;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetMetadata_; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetMetadata_;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule; import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetMetadataRepository;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetRepository;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetTagRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetFilterQueryRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility; import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.jpa.specifications.DistributionSetSpecification; import org.eclipse.hawkbit.repository.jpa.specifications.DistributionSetSpecification;
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder; import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications;
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper; import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
@@ -73,7 +86,7 @@ import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable; import org.springframework.retry.annotation.Retryable;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils; import org.springframework.util.ObjectUtils;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
@@ -99,6 +112,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
private final QuotaManagement quotaManagement; private final QuotaManagement quotaManagement;
private final DistributionSetMetadataRepository distributionSetMetadataRepository; private final DistributionSetMetadataRepository distributionSetMetadataRepository;
private final TargetRepository targetRepository;
private final TargetFilterQueryRepository targetFilterQueryRepository; private final TargetFilterQueryRepository targetFilterQueryRepository;
@@ -118,17 +132,19 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
private final Database database; private final Database database;
JpaDistributionSetManagement(final EntityManager entityManager, public JpaDistributionSetManagement(final EntityManager entityManager,
final DistributionSetRepository distributionSetRepository, final DistributionSetRepository distributionSetRepository,
final DistributionSetTagManagement distributionSetTagManagement, final SystemManagement systemManagement, final DistributionSetTagManagement distributionSetTagManagement, final SystemManagement systemManagement,
final DistributionSetTypeManagement distributionSetTypeManagement, final QuotaManagement quotaManagement, final DistributionSetTypeManagement distributionSetTypeManagement, final QuotaManagement quotaManagement,
final DistributionSetMetadataRepository distributionSetMetadataRepository, final DistributionSetMetadataRepository distributionSetMetadataRepository,
final TargetRepository targetRepository,
final TargetFilterQueryRepository targetFilterQueryRepository, final ActionRepository actionRepository, final TargetFilterQueryRepository targetFilterQueryRepository, final ActionRepository actionRepository,
final EventPublisherHolder eventPublisherHolder, final TenantAware tenantAware, final EventPublisherHolder eventPublisherHolder, final TenantAware tenantAware,
final VirtualPropertyReplacer virtualPropertyReplacer, final VirtualPropertyReplacer virtualPropertyReplacer,
final SoftwareModuleRepository softwareModuleRepository, final SoftwareModuleRepository softwareModuleRepository,
final DistributionSetTagRepository distributionSetTagRepository, final DistributionSetTagRepository distributionSetTagRepository,
final AfterTransactionCommitExecutor afterCommit, final Database database) { final AfterTransactionCommitExecutor afterCommit,
final Database database) {
this.entityManager = entityManager; this.entityManager = entityManager;
this.distributionSetRepository = distributionSetRepository; this.distributionSetRepository = distributionSetRepository;
this.distributionSetTagManagement = distributionSetTagManagement; this.distributionSetTagManagement = distributionSetTagManagement;
@@ -136,6 +152,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
this.distributionSetTypeManagement = distributionSetTypeManagement; this.distributionSetTypeManagement = distributionSetTypeManagement;
this.quotaManagement = quotaManagement; this.quotaManagement = quotaManagement;
this.distributionSetMetadataRepository = distributionSetMetadataRepository; this.distributionSetMetadataRepository = distributionSetMetadataRepository;
this.targetRepository = targetRepository;
this.targetFilterQueryRepository = targetFilterQueryRepository; this.targetFilterQueryRepository = targetFilterQueryRepository;
this.actionRepository = actionRepository; this.actionRepository = actionRepository;
this.eventPublisherHolder = eventPublisherHolder; this.eventPublisherHolder = eventPublisherHolder;
@@ -148,9 +165,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
} }
@Override @Override
public Optional<DistributionSet> getWithDetails(final long distid) { public Optional<DistributionSet> getWithDetails(final long id) {
return distributionSetRepository.findOne(DistributionSetSpecification.byId(distid)) return distributionSetRepository.findById(id).map(DistributionSet.class::cast);
.map(DistributionSet.class::cast);
} }
@Override @Override
@@ -159,66 +175,99 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
throw new EntityNotFoundException(DistributionSetType.class, typeId); throw new EntityNotFoundException(DistributionSetType.class, typeId);
} }
return distributionSetRepository.countByTypeId(typeId); return distributionSetRepository.count(DistributionSetSpecification.byType(typeId));
} }
@Override @Override
public List<Statistic> countRolloutsByStatusForDistributionSet(Long dsId) { public List<Statistic> countRolloutsByStatusForDistributionSet(final Long id) {
return distributionSetRepository.countRolloutsByStatusForDistributionSet(dsId).stream().map(Statistic.class::cast).collect(Collectors.toList()); assertDistributionSetExists(id);
return distributionSetRepository.countRolloutsByStatusForDistributionSet(id).stream()
.map(Statistic.class::cast).toList();
} }
@Override @Override
public List<Statistic> countActionsByStatusForDistributionSet(Long dsId) { public List<Statistic> countActionsByStatusForDistributionSet(final Long id) {
return distributionSetRepository.countActionsByStatusForDistributionSet(dsId).stream().map(Statistic.class::cast).collect(Collectors.toList()); assertDistributionSetExists(id);
return distributionSetRepository.countActionsByStatusForDistributionSet(id).stream()
.map(Statistic.class::cast).toList();
} }
@Override @Override
public Long countAutoAssignmentsForDistributionSet(Long dsId) { public Long countAutoAssignmentsForDistributionSet(final Long id) {
return distributionSetRepository.countAutoAssignmentsForDistributionSet(dsId); assertDistributionSetExists(id);
return distributionSetRepository.countAutoAssignmentsForDistributionSet(id);
} }
@Override @Override
@Transactional @Transactional
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSetTagAssignmentResult toggleTagAssignment(final Collection<Long> dsIds, final String tagName) { public DistributionSetTagAssignmentResult toggleTagAssignment(final Collection<Long> ids, final String tagName) {
final List<JpaDistributionSet> sets = findDistributionSetListWithDetails(dsIds); return updateTags(
ids,
if (sets.size() < dsIds.size()) { () -> distributionSetTagManagement
throw new EntityNotFoundException(DistributionSet.class, dsIds, .getByName(tagName)
sets.stream().map(DistributionSet::getId).collect(Collectors.toList())); .orElseThrow(() -> new EntityNotFoundException(DistributionSetTag.class, tagName)),
} (allDs, distributionSetTag) -> {
final List<JpaDistributionSet> toBeChangedDSs = allDs.stream().filter(set -> set.addTag(distributionSetTag))
final DistributionSetTag myTag = distributionSetTagManagement.getByName(tagName)
.orElseThrow(() -> new EntityNotFoundException(DistributionSetTag.class, tagName));
DistributionSetTagAssignmentResult result;
final List<JpaDistributionSet> toBeChangedDSs = sets.stream().filter(set -> set.addTag(myTag))
.collect(Collectors.toList()); .collect(Collectors.toList());
final DistributionSetTagAssignmentResult result;
// un-assignment case // un-assignment case
if (toBeChangedDSs.isEmpty()) { if (toBeChangedDSs.isEmpty()) {
for (final JpaDistributionSet set : sets) { for (final JpaDistributionSet set : allDs) {
if (set.removeTag(myTag)) { if (set.removeTag(distributionSetTag)) {
toBeChangedDSs.add(set); toBeChangedDSs.add(set);
} }
} }
result = new DistributionSetTagAssignmentResult(dsIds.size() - toBeChangedDSs.size(), result = new DistributionSetTagAssignmentResult(ids.size() - toBeChangedDSs.size(),
Collections.emptyList(), Collections.emptyList(),
Collections.unmodifiableList( Collections.unmodifiableList(
toBeChangedDSs.stream().map(distributionSetRepository::save).collect(Collectors.toList())), toBeChangedDSs.stream().map(distributionSetRepository::save).collect(Collectors.toList())),
myTag); distributionSetTag);
} else { } else {
result = new DistributionSetTagAssignmentResult(dsIds.size() - toBeChangedDSs.size(), result = new DistributionSetTagAssignmentResult(ids.size() - toBeChangedDSs.size(),
Collections.unmodifiableList( Collections.unmodifiableList(
toBeChangedDSs.stream().map(distributionSetRepository::save).collect(Collectors.toList())), toBeChangedDSs.stream().map(distributionSetRepository::save).collect(Collectors.toList())),
Collections.emptyList(), myTag); Collections.emptyList(), distributionSetTag);
}
return result;
});
} }
// no reason to persist the tag @Override
entityManager.detach(myTag); @Transactional
return result; @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<DistributionSet> assignTag(final Collection<Long> ids, final long dsTagId) {
return updateTags(
ids,
() -> distributionSetTagManagement.get(dsTagId).orElseThrow(() -> new EntityNotFoundException(DistributionSetTag.class, dsTagId)),
(allDs, distributionSetTag) -> {
allDs.forEach(ds -> ds.addTag(distributionSetTag));
return Collections.unmodifiableList(distributionSetRepository.saveAll(allDs));
});
}
private <T> T updateTags(
final Collection<Long> dsIds, final Supplier<DistributionSetTag> tagSupplier,
final BiFunction<List<JpaDistributionSet>, DistributionSetTag, T> updater) {
final List<JpaDistributionSet> allDs = findDistributionSetListWithDetails(dsIds);
if (allDs.size() < dsIds.size()) {
throw new EntityNotFoundException(DistributionSet.class, dsIds,
allDs.stream().map(DistributionSet::getId).toList());
}
final DistributionSetTag distributionSetTag = tagSupplier.get();
try {
return updater.apply(allDs, distributionSetTag);
} finally {
// No reason to save the tag
entityManager.detach(distributionSetTag);
}
} }
private List<JpaDistributionSet> findDistributionSetListWithDetails(final Collection<Long> distributionIdSet) { private List<JpaDistributionSet> findDistributionSetListWithDetails(final Collection<Long> distributionIdSet) {
@@ -247,9 +296,9 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
return distributionSetRepository.save(set); return distributionSetRepository.save(set);
} }
private JpaSoftwareModule findSoftwareModuleAndThrowExceptionIfNotFound(final Long moduleId) { private JpaSoftwareModule findSoftwareModuleAndThrowExceptionIfNotFound(final Long softwareModuleId) {
return softwareModuleRepository.findById(moduleId) return softwareModuleRepository.findById(softwareModuleId)
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, moduleId)); .orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, softwareModuleId));
} }
@Override @Override
@@ -257,11 +306,11 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void delete(final Collection<Long> distributionSetIDs) { public void delete(final Collection<Long> distributionSetIDs) {
final List<DistributionSet> setsFound = get(distributionSetIDs); getDistributionSets(distributionSetIDs); // throws EntityNotFoundException if any of these do not exists
final List<JpaDistributionSet> setsFound = distributionSetRepository.findAll(
AccessController.Operation.DELETE, distributionSetRepository.byIdsSpec(distributionSetIDs));
if (setsFound.size() < distributionSetIDs.size()) { if (setsFound.size() < distributionSetIDs.size()) {
throw new EntityNotFoundException(DistributionSet.class, distributionSetIDs, throw new InsufficientPermissionException("No DELETE access to some of distribution sets!");
setsFound.stream().map(DistributionSet::getId).collect(Collectors.toList()));
} }
final List<Long> assigned = distributionSetRepository final List<Long> assigned = distributionSetRepository
@@ -270,22 +319,21 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
// soft delete assigned // soft delete assigned
if (!assigned.isEmpty()) { if (!assigned.isEmpty()) {
final Long[] dsIds = assigned.toArray(new Long[assigned.size()]); final Long[] dsIds = assigned.toArray(new Long[0]);
distributionSetRepository.deleteDistributionSet(dsIds); distributionSetRepository.deleteDistributionSet(dsIds);
targetFilterQueryRepository.unsetAutoAssignDistributionSetAndActionType(dsIds); targetFilterQueryRepository.unsetAutoAssignDistributionSetAndActionTypeAndAccessContext(dsIds);
} }
// mark the rest as hard delete // mark the rest as hard delete
final List<Long> toHardDelete = distributionSetIDs.stream().filter(setId -> !assigned.contains(setId)) final List<Long> toHardDelete = distributionSetIDs.stream().filter(setId -> !assigned.contains(setId)).toList();
.collect(Collectors.toList());
// hard delete the rest if exists // hard delete the rest if exists
if (!toHardDelete.isEmpty()) { if (!toHardDelete.isEmpty()) {
targetFilterQueryRepository targetFilterQueryRepository.unsetAutoAssignDistributionSetAndActionTypeAndAccessContext(
.unsetAutoAssignDistributionSetAndActionType(toHardDelete.toArray(new Long[toHardDelete.size()])); toHardDelete.toArray(new Long[0]));
// don't give the delete statement an empty list, JPA/Oracle cannot // don't give the delete statement an empty list, JPA/Oracle cannot
// handle the empty list // handle the empty list
distributionSetRepository.deleteByIdIn(toHardDelete); distributionSetRepository.deleteAllById(toHardDelete);
} }
afterCommit.afterCommit(() -> distributionSetIDs.forEach(dsId -> eventPublisherHolder.getEventPublisher() afterCommit.afterCommit(() -> distributionSetIDs.forEach(dsId -> eventPublisherHolder.getEventPublisher()
@@ -299,11 +347,9 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSet create(final DistributionSetCreate c) { public DistributionSet create(final DistributionSetCreate c) {
final JpaDistributionSetCreate create = (JpaDistributionSetCreate) c; final JpaDistributionSetCreate create = (JpaDistributionSetCreate) c;
if (create.getType() == null) { setDefaultTypeIfMissing(create);
create.type(systemManagement.getTenantMetadata().getDefaultDsType().getKey());
}
return distributionSetRepository.save(create.build()); return distributionSetRepository.save(AccessController.Operation.CREATE, create.build());
} }
@Override @Override
@@ -311,28 +357,34 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<DistributionSet> create(final Collection<DistributionSetCreate> creates) { public List<DistributionSet> create(final Collection<DistributionSetCreate> creates) {
return creates.stream().map(this::create).collect(Collectors.toList()); final List<JpaDistributionSet> toCreate = creates.stream().map(JpaDistributionSetCreate.class::cast)
.map(this::setDefaultTypeIfMissing).map(JpaDistributionSetCreate::build).toList();
return Collections.unmodifiableList(distributionSetRepository.saveAll(AccessController.Operation.CREATE, toCreate));
}
private JpaDistributionSetCreate setDefaultTypeIfMissing(final JpaDistributionSetCreate create) {
if (create.getType() == null) {
create.type(systemManagement.getTenantMetadata().getDefaultDsType().getKey());
}
return create;
} }
@Override @Override
@Transactional @Transactional
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSet assignSoftwareModules(final long setId, final Collection<Long> moduleIds) { public DistributionSet assignSoftwareModules(final long id, final Collection<Long> softwareModuleId) {
final JpaDistributionSet set = (JpaDistributionSet) getValid(id);
assertDistributionSetIsNotAssignedToTargets(id);
assertSoftwareModuleQuota(id, softwareModuleId.size());
final Collection<JpaSoftwareModule> modules = softwareModuleRepository.findByIdIn(moduleIds); final Collection<JpaSoftwareModule> modules = softwareModuleRepository.findAllById(softwareModuleId);
if (modules.size() < softwareModuleId.size()) {
if (modules.size() < moduleIds.size()) { throw new EntityNotFoundException(SoftwareModule.class, softwareModuleId,
throw new EntityNotFoundException(SoftwareModule.class, moduleIds, modules.stream().map(SoftwareModule::getId).toList());
modules.stream().map(SoftwareModule::getId).collect(Collectors.toList()));
} }
assertDistributionSetIsNotAssignedToTargets(setId);
final JpaDistributionSet set = (JpaDistributionSet) getValid(setId);
assertSoftwareModuleQuota(setId, modules.size());
modules.forEach(set::addModule); modules.forEach(set::addModule);
return distributionSetRepository.save(set); return distributionSetRepository.save(set);
@@ -342,12 +394,11 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Transactional @Transactional
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSet unassignSoftwareModule(final long setId, final long moduleId) { public DistributionSet unassignSoftwareModule(final long id, final long moduleId) {
final JpaDistributionSet set = (JpaDistributionSet) getValid(setId); final JpaDistributionSet set = (JpaDistributionSet) getValid(id);
assertDistributionSetIsNotAssignedToTargets(id);
final JpaSoftwareModule module = findSoftwareModuleAndThrowExceptionIfNotFound(moduleId); final JpaSoftwareModule module = findSoftwareModuleAndThrowExceptionIfNotFound(moduleId);
assertDistributionSetIsNotAssignedToTargets(setId);
set.removeModule(module); set.removeModule(module);
return distributionSetRepository.save(set); return distributionSetRepository.save(set);
@@ -372,20 +423,25 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override @Override
public Slice<DistributionSet> findByCompleted(final Pageable pageReq, final Boolean complete) { public Slice<DistributionSet> findByCompleted(final Pageable pageReq, final Boolean complete) {
return JpaManagementHelper.findAllWithoutCountBySpec(distributionSetRepository, pageReq, final List<Specification<JpaDistributionSet>> specifications = buildSpecsByComplete(complete);
buildSpecsByComplete(complete));
}
private List<Specification<JpaDistributionSet>> buildSpecsByComplete(final Boolean complete) { return JpaManagementHelper.findAllWithoutCountBySpec(distributionSetRepository, pageReq, specifications);
return complete != null
? Arrays.asList(DistributionSetSpecification.isDeleted(false),
DistributionSetSpecification.isCompleted(complete))
: Collections.singletonList(DistributionSetSpecification.isDeleted(false));
} }
@Override @Override
public long countByCompleted(final Boolean complete) { public long countByCompleted(final Boolean complete) {
return JpaManagementHelper.countBySpec(distributionSetRepository, buildSpecsByComplete(complete)); final List<Specification<JpaDistributionSet>> specifications = buildSpecsByComplete(complete);
return JpaManagementHelper.countBySpec(distributionSetRepository, specifications);
}
private List<Specification<JpaDistributionSet>> buildSpecsByComplete(final Boolean complete) {
final List<Specification<JpaDistributionSet>> specifications = new ArrayList<>();
specifications.add(DistributionSetSpecification.isNotDeleted());
if (complete != null) {
specifications.add(DistributionSetSpecification.isCompleted(complete));
}
return specifications;
} }
@Override @Override
@@ -404,37 +460,34 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override @Override
public Optional<DistributionSet> getByNameAndVersion(final String distributionName, final String version) { public Optional<DistributionSet> getByNameAndVersion(final String distributionName, final String version) {
final Specification<JpaDistributionSet> spec = DistributionSetSpecification return distributionSetRepository
.equalsNameAndVersionIgnoreCase(distributionName, version); .findOne(DistributionSetSpecification.equalsNameAndVersionIgnoreCase(distributionName, version))
return distributionSetRepository.findOne(spec).map(DistributionSet.class::cast); .map(DistributionSet.class::cast);
} }
@Override @Override
public long count() { public long count() {
final Specification<JpaDistributionSet> spec = DistributionSetSpecification.isDeleted(Boolean.FALSE); return distributionSetRepository.count(DistributionSetSpecification.isNotDeleted());
return distributionSetRepository.count(SpecificationsBuilder.combineWithAnd(Arrays.asList(spec)));
} }
@Override @Override
@Transactional @Transactional
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<DistributionSetMetadata> createMetaData(final long dsId, final Collection<MetaData> md) { public List<DistributionSetMetadata> createMetaData(final long id, final Collection<MetaData> md) {
final JpaDistributionSet distributionSet = (JpaDistributionSet)getValid(id);
assertMetaDataQuota(id, md.size());
md.forEach(meta -> checkAndThrowIfDistributionSetMetadataAlreadyExists( md.forEach(meta -> checkAndThrowIfDistributionSetMetadataAlreadyExists(
new DsMetadataCompositeKey(dsId, meta.getKey()))); new DsMetadataCompositeKey(id, meta.getKey())));
assertMetaDataQuota(dsId, md.size()); JpaManagementHelper.touch(entityManager, distributionSetRepository, distributionSet);
final JpaDistributionSet set = JpaManagementHelper.touch(entityManager, distributionSetRepository, return md.stream()
(JpaDistributionSet) getValid(dsId));
return Collections.unmodifiableList(md.stream()
.map(meta -> distributionSetMetadataRepository .map(meta -> distributionSetMetadataRepository
.save(new JpaDistributionSetMetadata(meta.getKey(), set, meta.getValue()))) .save(new JpaDistributionSetMetadata(meta.getKey(), distributionSet, meta.getValue())))
.collect(Collectors.toList())); .collect(Collectors.toUnmodifiableList());
} }
private void assertMetaDataQuota(final Long dsId, final int requested) { private void assertMetaDataQuota(final Long dsId, final int requested) {
@@ -452,16 +505,16 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Transactional @Transactional
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSetMetadata updateMetaData(final long dsId, final MetaData md) { public DistributionSetMetadata updateMetaData(final long id, final MetaData md) {
// check if exists otherwise throw entity not found exception // check if exists otherwise throw entity not found exception
final JpaDistributionSetMetadata toUpdate = (JpaDistributionSetMetadata) getMetaDataByDistributionSetId(dsId, final JpaDistributionSetMetadata toUpdate = (JpaDistributionSetMetadata) getMetaDataByDistributionSetId(id,
md.getKey()).orElseThrow( md.getKey())
() -> new EntityNotFoundException(DistributionSetMetadata.class, dsId, md.getKey())); .orElseThrow(() -> new EntityNotFoundException(DistributionSetMetadata.class, id, md.getKey()));
toUpdate.setValue(md.getValue()); toUpdate.setValue(md.getValue());
// touch it to update the lock revision because we are modifying the // touch it to update the lock revision because we are modifying the
// DS indirectly // DS indirectly, it will, also check UPDATE access
JpaManagementHelper.touch(entityManager, distributionSetRepository, (JpaDistributionSet) getValid(dsId)); JpaManagementHelper.touch(entityManager, distributionSetRepository, (JpaDistributionSet) getValid(id));
return distributionSetMetadataRepository.save(toUpdate); return distributionSetMetadataRepository.save(toUpdate);
} }
@@ -469,11 +522,13 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Transactional @Transactional
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteMetaData(final long distributionSetId, final String key) { public void deleteMetaData(final long id, final String key) {
final JpaDistributionSetMetadata metadata = (JpaDistributionSetMetadata) getMetaDataByDistributionSetId( final JpaDistributionSetMetadata metadata = (JpaDistributionSetMetadata) getMetaDataByDistributionSetId(
distributionSetId, key).orElseThrow( id, key)
() -> new EntityNotFoundException(DistributionSetMetadata.class, distributionSetId, key)); .orElseThrow(() -> new EntityNotFoundException(DistributionSetMetadata.class, id, key));
// touch it to update the lock revision because we are modifying the
// DS indirectly, it will, also check UPDATE access
JpaManagementHelper.touch(entityManager, distributionSetRepository, JpaManagementHelper.touch(entityManager, distributionSetRepository,
(JpaDistributionSet) metadata.getDistributionSet()); (JpaDistributionSet) metadata.getDistributionSet());
distributionSetMetadataRepository.deleteById(metadata.getId()); distributionSetMetadataRepository.deleteById(metadata.getId());
@@ -481,11 +536,11 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override @Override
public Page<DistributionSetMetadata> findMetaDataByDistributionSetId(final Pageable pageable, public Page<DistributionSetMetadata> findMetaDataByDistributionSetId(final Pageable pageable,
final long distributionSetId) { final long id) {
throwExceptionIfDistributionSetDoesNotExist(distributionSetId); assertDistributionSetExists(id);
return JpaManagementHelper.findAllWithCountBySpec(distributionSetMetadataRepository, pageable, return JpaManagementHelper.findAllWithCountBySpec(distributionSetMetadataRepository, pageable,
Collections.singletonList(byDsIdSpec(distributionSetId))); Collections.singletonList(byDsIdSpec(id)));
} }
private Specification<JpaDistributionSetMetadata> byDsIdSpec(final long dsId) { private Specification<JpaDistributionSetMetadata> byDsIdSpec(final long dsId) {
@@ -494,51 +549,62 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
} }
@Override @Override
public long countMetaDataByDistributionSetId(final long setId) { public long countMetaDataByDistributionSetId(final long id) {
throwExceptionIfDistributionSetDoesNotExist(setId); assertDistributionSetExists(id);
return distributionSetMetadataRepository.countByDistributionSetId(setId); return distributionSetMetadataRepository.countByDistributionSetId(id);
} }
@Override @Override
public Page<DistributionSetMetadata> findMetaDataByDistributionSetIdAndRsql(final Pageable pageable, public Page<DistributionSetMetadata> findMetaDataByDistributionSetIdAndRsql(final Pageable pageable,
final long distributionSetId, final String rsqlParam) { final long id, final String rsqlParam) {
throwExceptionIfDistributionSetDoesNotExist(distributionSetId); assertDistributionSetExists(id);
final List<Specification<JpaDistributionSetMetadata>> specList = Arrays final List<Specification<JpaDistributionSetMetadata>> specList = Arrays
.asList(RSQLUtility.buildRsqlSpecification(rsqlParam, DistributionSetMetadataFields.class, .asList(RSQLUtility.buildRsqlSpecification(rsqlParam, DistributionSetMetadataFields.class,
virtualPropertyReplacer, database), byDsIdSpec(distributionSetId)); virtualPropertyReplacer, database), byDsIdSpec(id));
return JpaManagementHelper.findAllWithCountBySpec(distributionSetMetadataRepository, pageable, specList); return JpaManagementHelper.findAllWithCountBySpec(distributionSetMetadataRepository, pageable, specList);
} }
@Override @Override
public Optional<DistributionSetMetadata> getMetaDataByDistributionSetId(final long setId, final String key) { public Optional<DistributionSetMetadata> getMetaDataByDistributionSetId(final long id, final String key) {
throwExceptionIfDistributionSetDoesNotExist(setId); assertDistributionSetExists(id);
return distributionSetMetadataRepository.findById(new DsMetadataCompositeKey(setId, key)) return distributionSetMetadataRepository
.findById(new DsMetadataCompositeKey(id, key))
.map(DistributionSetMetadata.class::cast); .map(DistributionSetMetadata.class::cast);
} }
@Override @Override
public Optional<DistributionSet> getByAction(final long actionId) { public Optional<DistributionSet> getByAction(final long actionId) {
if (!actionRepository.existsById(actionId)) { return actionRepository
throw new EntityNotFoundException(Action.class, actionId); .findById(actionId)
.map(action -> {
if (!targetRepository.exists(TargetSpecifications.hasId(action.getTarget().getId()))) {
throw new InsufficientPermissionException("Target not accessible (or not found)!");
} }
return distributionSetRepository
return Optional.ofNullable(distributionSetRepository.findByActionId(actionId)); .findOne(DistributionSetSpecification.byActionId(actionId))
.orElseThrow(() ->
new InsufficientPermissionException("DistributionSet not accessible (or not found)!"));
})
.map(DistributionSet.class::cast)
.or(() -> {
throw new EntityNotFoundException(Action.class, actionId);
});
} }
@Override @Override
public boolean isInUse(final long setId) { public boolean isInUse(final long id) {
throwExceptionIfDistributionSetDoesNotExist(setId); assertDistributionSetExists(id);
return actionRepository.countByDistributionSetId(setId) > 0; return actionRepository.countByDistributionSetId(id) > 0;
} }
private static List<Specification<JpaDistributionSet>> buildDistributionSetSpecifications( private static List<Specification<JpaDistributionSet>> buildDistributionSetSpecifications(
final DistributionSetFilter distributionSetFilter) { final DistributionSetFilter distributionSetFilter) {
final List<Specification<JpaDistributionSet>> specList = Lists.newArrayListWithExpectedSize(9); final List<Specification<JpaDistributionSet>> specList = Lists.newArrayListWithExpectedSize(10);
Specification<JpaDistributionSet> spec; Specification<JpaDistributionSet> spec;
@@ -562,7 +628,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
specList.add(spec); specList.add(spec);
} }
if (!StringUtils.isEmpty(distributionSetFilter.getSearchText())) { if (!ObjectUtils.isEmpty(distributionSetFilter.getSearchText())) {
final String[] dsFilterNameAndVersionEntries = JpaManagementHelper final String[] dsFilterNameAndVersionEntries = JpaManagementHelper
.getFilterNameAndVersionEntries(distributionSetFilter.getSearchText().trim()); .getFilterNameAndVersionEntries(distributionSetFilter.getSearchText().trim());
spec = DistributionSetSpecification.likeNameAndVersion(dsFilterNameAndVersionEntries[0], spec = DistributionSetSpecification.likeNameAndVersion(dsFilterNameAndVersionEntries[0],
@@ -611,38 +677,12 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Transactional @Transactional
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<DistributionSet> assignTag(final Collection<Long> dsIds, final long dsTagId) { public DistributionSet unAssignTag(final long id, final long dsTagId) {
final List<JpaDistributionSet> allDs = findDistributionSetListWithDetails(dsIds); final JpaDistributionSet set = (JpaDistributionSet) getWithDetails(id)
.orElseThrow(() -> new EntityNotFoundException(DistributionSet.class, id));
if (allDs.size() < dsIds.size()) {
throw new EntityNotFoundException(DistributionSet.class, dsIds,
allDs.stream().map(DistributionSet::getId).collect(Collectors.toList()));
}
final DistributionSetTag distributionSetTag = distributionSetTagManagement.get(dsTagId) final DistributionSetTag distributionSetTag = distributionSetTagManagement.get(dsTagId)
.orElseThrow(() -> new EntityNotFoundException(DistributionSetTag.class, dsTagId)); .orElseThrow(() -> new EntityNotFoundException(DistributionSetTag.class, dsTagId));
allDs.forEach(ds -> ds.addTag(distributionSetTag));
final List<DistributionSet> result = Collections
.unmodifiableList(allDs.stream().map(distributionSetRepository::save).collect(Collectors.toList()));
// No reason to save the tag
entityManager.detach(distributionSetTag);
return result;
}
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSet unAssignTag(final long dsId, final long dsTagId) {
final JpaDistributionSet set = (JpaDistributionSet) getWithDetails(dsId)
.orElseThrow(() -> new EntityNotFoundException(DistributionSet.class, dsId));
final DistributionSetTag distributionSetTag = distributionSetTagManagement.get(dsTagId)
.orElseThrow(() -> new EntityNotFoundException(DistributionSetTag.class, dsTagId));
set.removeTag(distributionSetTag); set.removeTag(distributionSetTag);
final JpaDistributionSet result = distributionSetRepository.save(set); final JpaDistributionSet result = distributionSetRepository.save(set);
@@ -656,69 +696,59 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Transactional @Transactional
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void delete(final long setId) { public void delete(final long id) {
throwExceptionIfDistributionSetDoesNotExist(setId); delete(List.of(id));
delete(Collections.singletonList(setId));
}
private void throwExceptionIfDistributionSetDoesNotExist(final Long setId) {
if (!distributionSetRepository.existsById(setId)) {
throw new EntityNotFoundException(DistributionSet.class, setId);
}
} }
@Override @Override
public List<DistributionSet> get(final Collection<Long> ids) { public List<DistributionSet> get(final Collection<Long> ids) {
return Collections.unmodifiableList(distributionSetRepository.findAllById(ids)); return Collections.unmodifiableList(getDistributionSets(ids));
}
private List<JpaDistributionSet> getDistributionSets(final Collection<Long> ids) {
final List<JpaDistributionSet> foundDs = distributionSetRepository.findAllById(ids);
if (foundDs.size() != ids.size()) {
throw new EntityNotFoundException(
DistributionSet.class, ids, foundDs.stream().map(JpaDistributionSet::getId).toList());
}
return foundDs;
} }
@Override @Override
public Page<DistributionSet> findByTag(final Pageable pageable, final long tagId) { public Page<DistributionSet> findByTag(final Pageable pageable, final long tagId) {
throwEntityNotFoundExceptionIfDsTagDoesNotExist(tagId); assertDsTagExists(tagId);
return JpaManagementHelper.convertPage(distributionSetRepository.findByTag(pageable, tagId), pageable); return JpaManagementHelper.findAllWithCountBySpec(distributionSetRepository, pageable, List.of(
DistributionSetSpecification.hasTag(tagId)));
}
private void throwEntityNotFoundExceptionIfDsTagDoesNotExist(final Long tagId) {
if (!distributionSetTagRepository.existsById(tagId)) {
throw new EntityNotFoundException(DistributionSetTag.class, tagId);
}
} }
@Override @Override
public Page<DistributionSet> findByRsqlAndTag(final Pageable pageable, final String rsqlParam, final long tagId) { public Page<DistributionSet> findByRsqlAndTag(final Pageable pageable, final String rsqlParam, final long tagId) {
throwEntityNotFoundExceptionIfDsTagDoesNotExist(tagId); assertDsTagExists(tagId);
final List<Specification<JpaDistributionSet>> specList = Arrays.asList( return JpaManagementHelper.findAllWithCountBySpec(distributionSetRepository, pageable, List.of(
RSQLUtility.buildRsqlSpecification(rsqlParam, DistributionSetFields.class, virtualPropertyReplacer, RSQLUtility.buildRsqlSpecification(rsqlParam, DistributionSetFields.class, virtualPropertyReplacer,
database), database),
DistributionSetSpecification.hasTag(tagId), DistributionSetSpecification.isDeleted(false)); DistributionSetSpecification.hasTag(tagId), DistributionSetSpecification.isNotDeleted()));
return JpaManagementHelper.findAllWithCountBySpec(distributionSetRepository, pageable, specList);
} }
@Override @Override
public Slice<DistributionSet> findAll(final Pageable pageable) { public Slice<DistributionSet> findAll(final Pageable pageable) {
return JpaManagementHelper.findAllWithoutCountBySpec(distributionSetRepository, pageable, return JpaManagementHelper.findAllWithoutCountBySpec(distributionSetRepository, pageable, List.of(
Collections.singletonList(DistributionSetSpecification.isDeleted(false))); DistributionSetSpecification.isNotDeleted()));
} }
@Override @Override
public Page<DistributionSet> findByRsql(final Pageable pageable, final String rsqlParam) { public Page<DistributionSet> findByRsql(final Pageable pageable, final String rsqlParam) {
final List<Specification<JpaDistributionSet>> specList = Arrays.asList(RSQLUtility return JpaManagementHelper.findAllWithCountBySpec(distributionSetRepository, pageable, List.of(
.buildRsqlSpecification(rsqlParam, DistributionSetFields.class, virtualPropertyReplacer, database), RSQLUtility.buildRsqlSpecification(rsqlParam, DistributionSetFields.class, virtualPropertyReplacer,
DistributionSetSpecification.isDeleted(false)); database),
DistributionSetSpecification.isNotDeleted()));
return JpaManagementHelper.findAllWithCountBySpec(distributionSetRepository, pageable, specList);
} }
@Override @Override
public Optional<DistributionSet> get(final long id) { public Optional<DistributionSet> get(final long id) {
return distributionSetRepository.findById(id).map(d -> d); return distributionSetRepository.findById(id).map(DistributionSet.class::cast);
} }
@Override @Override
@@ -728,7 +758,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override @Override
public DistributionSet getOrElseThrowException(final long id) { public DistributionSet getOrElseThrowException(final long id) {
return get(id).orElseThrow(() -> new EntityNotFoundException(DistributionSet.class, id)); return getById(id);
} }
@Override @Override
@@ -757,10 +787,27 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override @Override
@Transactional @Transactional
public void invalidate(final DistributionSet set) { public void invalidate(final DistributionSet distributionSet) {
final JpaDistributionSet jpaSet = (JpaDistributionSet) set; final JpaDistributionSet jpaSet = (JpaDistributionSet) distributionSet;
jpaSet.invalidate(); jpaSet.invalidate();
distributionSetRepository.save(jpaSet); distributionSetRepository.save(AccessController.Operation.DELETE, jpaSet);
} }
private JpaDistributionSet getById(final long id) {
return distributionSetRepository
.findById(id)
.orElseThrow(() -> new EntityNotFoundException(DistributionSet.class, id));
}
private void assertDistributionSetExists(final long id) {
if (!distributionSetRepository.existsById(id)) {
throw new EntityNotFoundException(DistributionSet.class, id);
}
}
private void assertDsTagExists(final Long tagId) {
if (!distributionSetTagRepository.existsById(tagId)) {
throw new EntityNotFoundException(DistributionSetTag.class, tagId);
}
}
} }

View File

@@ -7,7 +7,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.management;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@@ -17,16 +17,21 @@ import java.util.stream.Collectors;
import org.eclipse.hawkbit.repository.DistributionSetTagFields; import org.eclipse.hawkbit.repository.DistributionSetTagFields;
import org.eclipse.hawkbit.repository.DistributionSetTagManagement; import org.eclipse.hawkbit.repository.DistributionSetTagManagement;
import org.eclipse.hawkbit.repository.TagFields;
import org.eclipse.hawkbit.repository.TargetTagManagement; import org.eclipse.hawkbit.repository.TargetTagManagement;
import org.eclipse.hawkbit.repository.builder.GenericTagUpdate; import org.eclipse.hawkbit.repository.builder.GenericTagUpdate;
import org.eclipse.hawkbit.repository.builder.TagCreate; import org.eclipse.hawkbit.repository.builder.TagCreate;
import org.eclipse.hawkbit.repository.builder.TagUpdate; import org.eclipse.hawkbit.repository.builder.TagUpdate;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.eclipse.hawkbit.repository.jpa.builder.JpaTagCreate; import org.eclipse.hawkbit.repository.jpa.builder.JpaTagCreate;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants; import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetRepository;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetTagRepository;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility; import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.jpa.specifications.DistributionSetTagSpecifications;
import org.eclipse.hawkbit.repository.jpa.specifications.TagSpecification; import org.eclipse.hawkbit.repository.jpa.specifications.TagSpecification;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetTag; import org.eclipse.hawkbit.repository.model.DistributionSetTag;
@@ -58,7 +63,7 @@ public class JpaDistributionSetTagManagement implements DistributionSetTagManage
private final Database database; private final Database database;
JpaDistributionSetTagManagement(final DistributionSetTagRepository distributionSetTagRepository, public JpaDistributionSetTagManagement(final DistributionSetTagRepository distributionSetTagRepository,
final DistributionSetRepository distributionSetRepository, final DistributionSetRepository distributionSetRepository,
final VirtualPropertyReplacer virtualPropertyReplacer, final Database database) { final VirtualPropertyReplacer virtualPropertyReplacer, final Database database) {
this.distributionSetTagRepository = distributionSetTagRepository; this.distributionSetTagRepository = distributionSetTagRepository;
@@ -95,7 +100,7 @@ public class JpaDistributionSetTagManagement implements DistributionSetTagManage
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSetTag create(final TagCreate c) { public DistributionSetTag create(final TagCreate c) {
final JpaTagCreate create = (JpaTagCreate) c; final JpaTagCreate create = (JpaTagCreate) c;
return distributionSetTagRepository.save(create.buildDistributionSetTag()); return distributionSetTagRepository.save(AccessController.Operation.CREATE, create.buildDistributionSetTag());
} }
@Override @Override
@@ -103,13 +108,10 @@ public class JpaDistributionSetTagManagement implements DistributionSetTagManage
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<DistributionSetTag> create(final Collection<TagCreate> dst) { public List<DistributionSetTag> create(final Collection<TagCreate> dst) {
final List<JpaDistributionSetTag> toCreate = dst.stream().map(JpaTagCreate.class::cast)
@SuppressWarnings({ "rawtypes", "unchecked" }) .map(JpaTagCreate::buildDistributionSetTag).toList();
final Collection<JpaTagCreate> creates = (Collection) dst; return Collections
.unmodifiableList(distributionSetTagRepository.saveAll(AccessController.Operation.CREATE, toCreate));
return Collections.unmodifiableList(
creates.stream().map(create -> distributionSetTagRepository.save(create.buildDistributionSetTag()))
.collect(Collectors.toList()));
} }
@Override @Override
@@ -117,11 +119,11 @@ public class JpaDistributionSetTagManagement implements DistributionSetTagManage
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void delete(final String tagName) { public void delete(final String tagName) {
if (!distributionSetTagRepository.existsByName(tagName)) { final JpaDistributionSetTag dsTag = distributionSetTagRepository
throw new EntityNotFoundException(DistributionSetTag.class, tagName); .findOne(DistributionSetTagSpecifications.byName(tagName))
} .orElseThrow(() -> new EntityNotFoundException(DistributionSetTag.class, tagName));
distributionSetTagRepository.deleteByName(tagName); distributionSetTagRepository.delete(dsTag);
} }
@Override @Override
@@ -139,13 +141,13 @@ public class JpaDistributionSetTagManagement implements DistributionSetTagManage
} }
@Override @Override
public Page<DistributionSetTag> findByDistributionSet(final Pageable pageable, final long setId) { public Page<DistributionSetTag> findByDistributionSet(final Pageable pageable, final long distributionSetId) {
if (!distributionSetRepository.existsById(setId)) { if (!distributionSetRepository.existsById(distributionSetId)) {
throw new EntityNotFoundException(DistributionSet.class, setId); throw new EntityNotFoundException(DistributionSet.class, distributionSetId);
} }
return JpaManagementHelper.findAllWithCountBySpec(distributionSetTagRepository, pageable, return JpaManagementHelper.findAllWithCountBySpec(distributionSetTagRepository, pageable,
Collections.singletonList(TagSpecification.ofDistributionSet(setId))); Collections.singletonList(TagSpecification.ofDistributionSet(distributionSetId)));
} }
@Override @Override
@@ -190,5 +192,4 @@ public class JpaDistributionSetTagManagement implements DistributionSetTagManage
public long count() { public long count() {
return distributionSetTagRepository.count(); return distributionSetTagRepository.count();
} }
} }

View File

@@ -7,9 +7,8 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.management;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@@ -29,11 +28,17 @@ import org.eclipse.hawkbit.repository.builder.GenericDistributionSetTypeUpdate;
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException; import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException; import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.eclipse.hawkbit.repository.jpa.builder.JpaDistributionSetTypeCreate; import org.eclipse.hawkbit.repository.jpa.builder.JpaDistributionSetTypeCreate;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants; import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType; import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType; import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetRepository;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetTypeRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleTypeRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetTypeRepository;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility; import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.jpa.specifications.DistributionSetTypeSpecification; import org.eclipse.hawkbit.repository.jpa.specifications.DistributionSetTypeSpecification;
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper; import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
@@ -73,7 +78,7 @@ public class JpaDistributionSetTypeManagement implements DistributionSetTypeMana
private final QuotaManagement quotaManagement; private final QuotaManagement quotaManagement;
JpaDistributionSetTypeManagement(final DistributionSetTypeRepository distributionSetTypeRepository, public JpaDistributionSetTypeManagement(final DistributionSetTypeRepository distributionSetTypeRepository,
final SoftwareModuleTypeRepository softwareModuleTypeRepository, final SoftwareModuleTypeRepository softwareModuleTypeRepository,
final DistributionSetRepository distributionSetRepository, final TargetTypeRepository targetTypeRepository, final DistributionSetRepository distributionSetRepository, final TargetTypeRepository targetTypeRepository,
final VirtualPropertyReplacer virtualPropertyReplacer, final Database database, final VirtualPropertyReplacer virtualPropertyReplacer, final Database database,
@@ -100,7 +105,7 @@ public class JpaDistributionSetTypeManagement implements DistributionSetTypeMana
update.getColour().ifPresent(type::setColour); update.getColour().ifPresent(type::setColour);
if (hasModuleChanges(update)) { if (hasModuleChanges(update)) {
checkDistributionSetTypeSoftwareModuleTypesIsAllowedToModify(update.getId()); checkDistributionSetTypeNotAssigned(update.getId());
final Collection<Long> currentMandatorySmTypeIds = type.getMandatoryModuleTypes().stream() final Collection<Long> currentMandatorySmTypeIds = type.getMandatoryModuleTypes().stream()
.map(SoftwareModuleType::getId).collect(Collectors.toSet()); .map(SoftwareModuleType::getId).collect(Collectors.toSet());
@@ -148,45 +153,35 @@ public class JpaDistributionSetTypeManagement implements DistributionSetTypeMana
@Transactional @Transactional
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSetType assignMandatorySoftwareModuleTypes(final long dsTypeId, public DistributionSetType assignMandatorySoftwareModuleTypes(final long id,
final Collection<Long> softwareModulesTypeIds) { final Collection<Long> softwareModuleTypeIds) {
final Collection<JpaSoftwareModuleType> modules = softwareModuleTypeRepository return assignSoftwareModuleTypes(id, softwareModuleTypeIds, true);
.findAllById(softwareModulesTypeIds);
if (modules.size() < softwareModulesTypeIds.size()) {
throw new EntityNotFoundException(SoftwareModuleType.class, softwareModulesTypeIds,
modules.stream().map(SoftwareModuleType::getId).collect(Collectors.toList()));
}
final JpaDistributionSetType type = findDistributionSetTypeAndThrowExceptionIfNotFound(dsTypeId);
checkDistributionSetTypeSoftwareModuleTypesIsAllowedToModify(dsTypeId);
assertSoftwareModuleTypeQuota(dsTypeId, softwareModulesTypeIds.size());
modules.forEach(type::addMandatoryModuleType);
return distributionSetTypeRepository.save(type);
} }
@Override @Override
@Transactional @Transactional
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSetType assignOptionalSoftwareModuleTypes(final long dsTypeId, public DistributionSetType assignOptionalSoftwareModuleTypes(final long id,
final Collection<Long> softwareModulesTypeIds) { final Collection<Long> softwareModulesTypeIds) {
return assignSoftwareModuleTypes(id, softwareModulesTypeIds, false);
}
final Collection<JpaSoftwareModuleType> modules = softwareModuleTypeRepository private DistributionSetType assignSoftwareModuleTypes(
.findAllById(softwareModulesTypeIds); final long dsTypeId, final Collection<Long> softwareModulesTypeIds, final boolean mandatory) {
final Collection<JpaSoftwareModuleType> foundModules =
if (modules.size() < softwareModulesTypeIds.size()) { softwareModuleTypeRepository.findAllById(softwareModulesTypeIds);
if (foundModules.size() < softwareModulesTypeIds.size()) {
throw new EntityNotFoundException(SoftwareModuleType.class, softwareModulesTypeIds, throw new EntityNotFoundException(SoftwareModuleType.class, softwareModulesTypeIds,
modules.stream().map(SoftwareModuleType::getId).collect(Collectors.toList())); foundModules.stream().map(SoftwareModuleType::getId).toList());
} }
final JpaDistributionSetType type = findDistributionSetTypeAndThrowExceptionIfNotFound(dsTypeId); final JpaDistributionSetType type = findDistributionSetTypeAndThrowExceptionIfNotFound(dsTypeId);
checkDistributionSetTypeSoftwareModuleTypesIsAllowedToModify(dsTypeId);
checkDistributionSetTypeNotAssigned(dsTypeId);
assertSoftwareModuleTypeQuota(dsTypeId, softwareModulesTypeIds.size()); assertSoftwareModuleTypeQuota(dsTypeId, softwareModulesTypeIds.size());
modules.forEach(type::addOptionalModuleType); foundModules.forEach(mandatory ? type::addMandatoryModuleType : type::addOptionalModuleType);
return distributionSetTypeRepository.save(type); return distributionSetTypeRepository.save(type);
} }
@@ -213,10 +208,10 @@ public class JpaDistributionSetTypeManagement implements DistributionSetTypeMana
@Transactional @Transactional
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSetType unassignSoftwareModuleType(final long dsTypeId, final long softwareModuleTypeId) { public DistributionSetType unassignSoftwareModuleType(final long id, final long softwareModuleTypeId) {
final JpaDistributionSetType type = findDistributionSetTypeAndThrowExceptionIfNotFound(dsTypeId); final JpaDistributionSetType type = findDistributionSetTypeAndThrowExceptionIfNotFound(id);
checkDistributionSetTypeSoftwareModuleTypesIsAllowedToModify(dsTypeId); checkDistributionSetTypeNotAssigned(id);
type.removeModuleType(softwareModuleTypeId); type.removeModuleType(softwareModuleTypeId);
@@ -225,35 +220,33 @@ public class JpaDistributionSetTypeManagement implements DistributionSetTypeMana
@Override @Override
public Page<DistributionSetType> findByRsql(final Pageable pageable, final String rsqlParam) { public Page<DistributionSetType> findByRsql(final Pageable pageable, final String rsqlParam) {
return JpaManagementHelper return JpaManagementHelper.findAllWithCountBySpec(distributionSetTypeRepository, pageable, List.of(
.findAllWithCountBySpec(distributionSetTypeRepository, pageable, RSQLUtility.buildRsqlSpecification(rsqlParam, DistributionSetTypeFields.class, virtualPropertyReplacer,
Arrays.asList( database),
RSQLUtility.buildRsqlSpecification(rsqlParam, DistributionSetTypeFields.class, DistributionSetTypeSpecification.isNotDeleted()));
virtualPropertyReplacer, database),
DistributionSetTypeSpecification.isDeleted(false)));
} }
@Override @Override
public Slice<DistributionSetType> findAll(final Pageable pageable) { public Slice<DistributionSetType> findAll(final Pageable pageable) {
return JpaManagementHelper.findAllWithoutCountBySpec(distributionSetTypeRepository, pageable, return JpaManagementHelper.findAllWithoutCountBySpec(distributionSetTypeRepository, pageable, List.of(
Collections.singletonList(DistributionSetTypeSpecification.isDeleted(false))); DistributionSetTypeSpecification.isNotDeleted()));
} }
@Override @Override
public long count() { public long count() {
return distributionSetTypeRepository.countByDeleted(false); return distributionSetTypeRepository.count(DistributionSetTypeSpecification.isNotDeleted());
} }
@Override @Override
public Optional<DistributionSetType> getByName(final String name) { public Optional<DistributionSetType> getByName(final String name) {
return distributionSetTypeRepository.findOne(DistributionSetTypeSpecification.byName(name)) return distributionSetTypeRepository
.map(dst -> (DistributionSetType) dst); .findOne(DistributionSetTypeSpecification.byName(name)).map(DistributionSetType.class::cast);
} }
@Override @Override
public Optional<DistributionSetType> getByKey(final String key) { public Optional<DistributionSetType> getByKey(final String key) {
return distributionSetTypeRepository.findOne(DistributionSetTypeSpecification.byKey(key)) return distributionSetTypeRepository
.map(dst -> (DistributionSetType) dst); .findOne(DistributionSetTypeSpecification.byKey(key)).map(DistributionSetType.class::cast);
} }
@Override @Override
@@ -261,26 +254,26 @@ public class JpaDistributionSetTypeManagement implements DistributionSetTypeMana
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSetType create(final DistributionSetTypeCreate c) { public DistributionSetType create(final DistributionSetTypeCreate c) {
final JpaDistributionSetTypeCreate create = (JpaDistributionSetTypeCreate) c; final JpaDistributionSetType distributionSetType = ((JpaDistributionSetTypeCreate) c).build();
return distributionSetTypeRepository.save(create.build()); return distributionSetTypeRepository.save(AccessController.Operation.CREATE, distributionSetType);
} }
@Override @Override
@Transactional @Transactional
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void delete(final long typeId) { public void delete(final long id) {
final JpaDistributionSetType toDelete = distributionSetTypeRepository.findById(typeId) final JpaDistributionSetType toDelete = distributionSetTypeRepository.findById(id)
.orElseThrow(() -> new EntityNotFoundException(DistributionSetType.class, typeId)); .orElseThrow(() -> new EntityNotFoundException(DistributionSetType.class, id));
unassignDsTypeFromTargetTypes(typeId); unassignDsTypeFromTargetTypes(id);
if (distributionSetRepository.countByTypeId(typeId) > 0) { if (distributionSetRepository.countByTypeId(id) > 0) {
toDelete.setDeleted(true); toDelete.setDeleted(true);
distributionSetTypeRepository.save(toDelete); distributionSetTypeRepository.save(AccessController.Operation.DELETE, toDelete);
} else { } else {
distributionSetTypeRepository.deleteById(typeId); distributionSetTypeRepository.deleteById(id);
} }
} }
@@ -297,7 +290,11 @@ public class JpaDistributionSetTypeManagement implements DistributionSetTypeMana
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<DistributionSetType> create(final Collection<DistributionSetTypeCreate> types) { public List<DistributionSetType> create(final Collection<DistributionSetTypeCreate> types) {
return types.stream().map(this::create).collect(Collectors.toList()); final List<JpaDistributionSetType> typesToCreate = types.stream().map(JpaDistributionSetTypeCreate.class::cast)
.map(JpaDistributionSetTypeCreate::build).toList();
return Collections.unmodifiableList(
distributionSetTypeRepository.saveAll(AccessController.Operation.CREATE, typesToCreate));
} }
private JpaDistributionSetType findDistributionSetTypeAndThrowExceptionIfNotFound(final Long setId) { private JpaDistributionSetType findDistributionSetTypeAndThrowExceptionIfNotFound(final Long setId) {
@@ -309,10 +306,10 @@ public class JpaDistributionSetTypeManagement implements DistributionSetTypeMana
return update.getOptional().isPresent() || update.getMandatory().isPresent(); return update.getOptional().isPresent() || update.getMandatory().isPresent();
} }
private void checkDistributionSetTypeSoftwareModuleTypesIsAllowedToModify(final Long type) { private void checkDistributionSetTypeNotAssigned(final Long id) {
if (distributionSetRepository.countByTypeId(type) > 0) { if (distributionSetRepository.countByTypeId(id) > 0) {
throw new EntityReadOnlyException(String.format( throw new EntityReadOnlyException(String.format(
"distribution set type %s is already assigned to distribution sets and cannot be changed", type)); "Distribution set type %s is already assigned to distribution sets and cannot be changed!", id));
} }
} }
@@ -321,14 +318,7 @@ public class JpaDistributionSetTypeManagement implements DistributionSetTypeMana
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void delete(final Collection<Long> ids) { public void delete(final Collection<Long> ids) {
final List<JpaDistributionSetType> setsFound = distributionSetTypeRepository.findAllById(ids); distributionSetTypeRepository.deleteAllById(ids);
if (setsFound.size() < ids.size()) {
throw new EntityNotFoundException(DistributionSetType.class, ids,
setsFound.stream().map(DistributionSetType::getId).collect(Collectors.toList()));
}
distributionSetTypeRepository.deleteAll(setsFound);
} }
@Override @Override
@@ -338,12 +328,11 @@ public class JpaDistributionSetTypeManagement implements DistributionSetTypeMana
@Override @Override
public Optional<DistributionSetType> get(final long id) { public Optional<DistributionSetType> get(final long id) {
return distributionSetTypeRepository.findById(id).map(dst -> (DistributionSetType) dst); return distributionSetTypeRepository.findById(id).map(DistributionSetType.class::cast);
} }
@Override @Override
public boolean exists(final long id) { public boolean exists(final long id) {
return distributionSetTypeRepository.existsById(id); return distributionSetTypeRepository.existsById(id);
} }
} }

View File

@@ -7,7 +7,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.management;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
@@ -31,6 +31,7 @@ import org.eclipse.hawkbit.repository.RolloutGroupManagement;
import org.eclipse.hawkbit.repository.RolloutStatusCache; import org.eclipse.hawkbit.repository.RolloutStatusCache;
import org.eclipse.hawkbit.repository.TargetFields; import org.eclipse.hawkbit.repository.TargetFields;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction; import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction_; import org.eclipse.hawkbit.repository.jpa.model.JpaAction_;
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup; import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup;
@@ -40,6 +41,10 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_; import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
import org.eclipse.hawkbit.repository.jpa.model.RolloutTargetGroup; import org.eclipse.hawkbit.repository.jpa.model.RolloutTargetGroup;
import org.eclipse.hawkbit.repository.jpa.model.RolloutTargetGroup_; import org.eclipse.hawkbit.repository.jpa.model.RolloutTargetGroup_;
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
import org.eclipse.hawkbit.repository.jpa.repository.RolloutGroupRepository;
import org.eclipse.hawkbit.repository.jpa.repository.RolloutRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility; import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications; import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
@@ -85,7 +90,7 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
private final Database database; private final Database database;
JpaRolloutGroupManagement(final RolloutGroupRepository rolloutGroupRepository, public JpaRolloutGroupManagement(final RolloutGroupRepository rolloutGroupRepository,
final RolloutRepository rolloutRepository, final ActionRepository actionRepository, final RolloutRepository rolloutRepository, final ActionRepository actionRepository,
final TargetRepository targetRepository, final EntityManager entityManager, final TargetRepository targetRepository, final EntityManager entityManager,
final VirtualPropertyReplacer virtualPropertyReplacer, final RolloutStatusCache rolloutStatusCache, final VirtualPropertyReplacer virtualPropertyReplacer, final RolloutStatusCache rolloutStatusCache,

View File

@@ -7,7 +7,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.management;
import static org.eclipse.hawkbit.repository.jpa.builder.JpaRolloutGroupCreate.addSuccessAndErrorConditionsAndActions; import static org.eclipse.hawkbit.repository.jpa.builder.JpaRolloutGroupCreate.addSuccessAndErrorConditionsAndActions;
@@ -33,6 +33,7 @@ import org.eclipse.hawkbit.repository.RolloutManagement;
import org.eclipse.hawkbit.repository.RolloutStatusCache; import org.eclipse.hawkbit.repository.RolloutStatusCache;
import org.eclipse.hawkbit.repository.TargetManagement; import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.ContextAware;
import org.eclipse.hawkbit.repository.builder.GenericRolloutUpdate; import org.eclipse.hawkbit.repository.builder.GenericRolloutUpdate;
import org.eclipse.hawkbit.repository.builder.RolloutCreate; import org.eclipse.hawkbit.repository.builder.RolloutCreate;
import org.eclipse.hawkbit.repository.builder.RolloutGroupCreate; import org.eclipse.hawkbit.repository.builder.RolloutGroupCreate;
@@ -41,12 +42,16 @@ import org.eclipse.hawkbit.repository.event.remote.entity.RolloutGroupCreatedEve
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException; import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
import org.eclipse.hawkbit.repository.exception.RolloutIllegalStateException; import org.eclipse.hawkbit.repository.exception.RolloutIllegalStateException;
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
import org.eclipse.hawkbit.repository.jpa.builder.JpaRolloutGroupCreate; import org.eclipse.hawkbit.repository.jpa.builder.JpaRolloutGroupCreate;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants; import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor; import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout; import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup; import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup;
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout_; import org.eclipse.hawkbit.repository.jpa.model.JpaRollout_;
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
import org.eclipse.hawkbit.repository.jpa.repository.RolloutGroupRepository;
import org.eclipse.hawkbit.repository.jpa.repository.RolloutRepository;
import org.eclipse.hawkbit.repository.jpa.rollout.condition.StartNextGroupRolloutGroupSuccessAction; import org.eclipse.hawkbit.repository.jpa.rollout.condition.StartNextGroupRolloutGroupSuccessAction;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility; import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.jpa.specifications.RolloutSpecification; import org.eclipse.hawkbit.repository.jpa.specifications.RolloutSpecification;
@@ -132,6 +137,7 @@ public class JpaRolloutManagement implements RolloutManagement {
private final TenantConfigurationManagement tenantConfigurationManagement; private final TenantConfigurationManagement tenantConfigurationManagement;
private final SystemSecurityContext systemSecurityContext; private final SystemSecurityContext systemSecurityContext;
private final EventPublisherHolder eventPublisherHolder; private final EventPublisherHolder eventPublisherHolder;
private final ContextAware contextAware;
private final Database database; private final Database database;
public JpaRolloutManagement(final TargetManagement targetManagement, public JpaRolloutManagement(final TargetManagement targetManagement,
@@ -139,21 +145,22 @@ public class JpaRolloutManagement implements RolloutManagement {
final VirtualPropertyReplacer virtualPropertyReplacer, final Database database, final VirtualPropertyReplacer virtualPropertyReplacer, final Database database,
final RolloutApprovalStrategy rolloutApprovalStrategy, final RolloutApprovalStrategy rolloutApprovalStrategy,
final TenantConfigurationManagement tenantConfigurationManagement, final TenantConfigurationManagement tenantConfigurationManagement,
final SystemSecurityContext systemSecurityContext) { final SystemSecurityContext systemSecurityContext,
final ContextAware contextAware) {
this.targetManagement = targetManagement; this.targetManagement = targetManagement;
this.distributionSetManagement = distributionSetManagement; this.distributionSetManagement = distributionSetManagement;
this.virtualPropertyReplacer = virtualPropertyReplacer; this.virtualPropertyReplacer = virtualPropertyReplacer;
this.database = database;
this.rolloutApprovalStrategy = rolloutApprovalStrategy; this.rolloutApprovalStrategy = rolloutApprovalStrategy;
this.tenantConfigurationManagement = tenantConfigurationManagement; this.tenantConfigurationManagement = tenantConfigurationManagement;
this.systemSecurityContext = systemSecurityContext; this.systemSecurityContext = systemSecurityContext;
this.eventPublisherHolder = eventPublisherHolder; this.eventPublisherHolder = eventPublisherHolder;
this.database = database; this.contextAware = contextAware;
} }
@Override @Override
public Page<Rollout> findAll(final Pageable pageable, final boolean deleted) { public Page<Rollout> findAll(final Pageable pageable, final boolean deleted) {
return JpaManagementHelper.findAllWithCountBySpec(rolloutRepository, pageable, return JpaManagementHelper.findAllWithCountBySpec(rolloutRepository, pageable, Collections
Collections
.singletonList(RolloutSpecification.isDeletedWithDistributionSet(deleted, pageable.getSort()))); .singletonList(RolloutSpecification.isDeletedWithDistributionSet(deleted, pageable.getSort())));
} }
@@ -212,6 +219,7 @@ public class JpaRolloutManagement implements RolloutManagement {
throw new ValidationException(errMsg); throw new ValidationException(errMsg);
} }
rollout.setTotalTargets(totalTargets); rollout.setTotalTargets(totalTargets);
contextAware.getCurrentContext().ifPresent(rollout::setAccessControlContext);
return rolloutRepository.save(rollout); return rolloutRepository.save(rollout);
} }
@@ -301,8 +309,8 @@ public class JpaRolloutManagement implements RolloutManagement {
} }
/** /**
* In case the given group is missing conditions or actions, they will be * In case the given group is missing conditions or actions, they will be set
* set from the supplied default conditions. * from the supplied default conditions.
* *
* @param create * @param create
* group to check * group to check
@@ -500,7 +508,7 @@ public class JpaRolloutManagement implements RolloutManagement {
update.getActionType().ifPresent(rollout::setActionType); update.getActionType().ifPresent(rollout::setActionType);
update.getForcedTime().ifPresent(rollout::setForcedTime); update.getForcedTime().ifPresent(rollout::setForcedTime);
update.getWeight().ifPresent(rollout::setWeight); update.getWeight().ifPresent(rollout::setWeight);
// reseting back to manual start is done by setting start at time to // resetting back to manual start is done by setting start at time to
// null // null
rollout.setStartAt(update.getStartAt().orElse(null)); rollout.setStartAt(update.getStartAt().orElse(null));
update.getSet().ifPresent(setId -> { update.getSet().ifPresent(setId -> {
@@ -586,6 +594,7 @@ public class JpaRolloutManagement implements RolloutManagement {
return fromCache; return fromCache;
} }
@Override @Override
public void setRolloutStatusDetails(final Slice<Rollout> rollouts) { public void setRolloutStatusDetails(final Slice<Rollout> rollouts) {
final List<Long> rolloutIds = rollouts.getContent().stream().map(Rollout::getId).collect(Collectors.toList()); final List<Long> rolloutIds = rollouts.getContent().stream().map(Rollout::getId).collect(Collectors.toList());
@@ -768,5 +777,4 @@ public class JpaRolloutManagement implements RolloutManagement {
} }
private record TargetCount(long total, String filter) {} private record TargetCount(long total, String filter) {}
} }

View File

@@ -7,7 +7,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.management;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@@ -32,7 +32,6 @@ import javax.persistence.criteria.Order;
import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root; import javax.persistence.criteria.Root;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.repository.ArtifactEncryptionService; import org.eclipse.hawkbit.repository.ArtifactEncryptionService;
import org.eclipse.hawkbit.repository.ArtifactManagement; import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.QuotaManagement; import org.eclipse.hawkbit.repository.QuotaManagement;
@@ -48,6 +47,9 @@ import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataUpdate;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleUpdate; import org.eclipse.hawkbit.repository.builder.SoftwareModuleUpdate;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException; import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.eclipse.hawkbit.repository.jpa.builder.JpaSoftwareModuleCreate; import org.eclipse.hawkbit.repository.jpa.builder.JpaSoftwareModuleCreate;
import org.eclipse.hawkbit.repository.jpa.builder.JpaSoftwareModuleMetadataCreate; import org.eclipse.hawkbit.repository.jpa.builder.JpaSoftwareModuleMetadataCreate;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants; import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
@@ -58,6 +60,10 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleMetadata;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleMetadata_; import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleMetadata_;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule_; import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule_;
import org.eclipse.hawkbit.repository.jpa.model.SwMetadataCompositeKey; import org.eclipse.hawkbit.repository.jpa.model.SwMetadataCompositeKey;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleMetadataRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleTypeRepository;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility; import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.jpa.specifications.SoftwareModuleSpecification; import org.eclipse.hawkbit.repository.jpa.specifications.SoftwareModuleSpecification;
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper; import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
@@ -81,13 +87,10 @@ import org.springframework.data.jpa.repository.query.QueryUtils;
import org.springframework.orm.jpa.vendor.Database; import org.springframework.orm.jpa.vendor.Database;
import org.springframework.retry.annotation.Backoff; import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable; import org.springframework.retry.annotation.Retryable;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils; import org.springframework.util.ObjectUtils;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import com.google.common.collect.Lists;
/** /**
* JPA implementation of {@link SoftwareModuleManagement}. * JPA implementation of {@link SoftwareModuleManagement}.
* *
@@ -97,23 +100,14 @@ import com.google.common.collect.Lists;
public class JpaSoftwareModuleManagement implements SoftwareModuleManagement { public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
private final EntityManager entityManager; private final EntityManager entityManager;
private final DistributionSetRepository distributionSetRepository; private final DistributionSetRepository distributionSetRepository;
private final SoftwareModuleRepository softwareModuleRepository; private final SoftwareModuleRepository softwareModuleRepository;
private final SoftwareModuleMetadataRepository softwareModuleMetadataRepository; private final SoftwareModuleMetadataRepository softwareModuleMetadataRepository;
private final SoftwareModuleTypeRepository softwareModuleTypeRepository; private final SoftwareModuleTypeRepository softwareModuleTypeRepository;
private final AuditorAware<String> auditorProvider; private final AuditorAware<String> auditorProvider;
private final ArtifactManagement artifactManagement; private final ArtifactManagement artifactManagement;
private final QuotaManagement quotaManagement; private final QuotaManagement quotaManagement;
private final VirtualPropertyReplacer virtualPropertyReplacer; private final VirtualPropertyReplacer virtualPropertyReplacer;
private final Database database; private final Database database;
public JpaSoftwareModuleManagement(final EntityManager entityManager, public JpaSoftwareModuleManagement(final EntityManager entityManager,
@@ -122,7 +116,8 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
final SoftwareModuleMetadataRepository softwareModuleMetadataRepository, final SoftwareModuleMetadataRepository softwareModuleMetadataRepository,
final SoftwareModuleTypeRepository softwareModuleTypeRepository, final AuditorAware<String> auditorProvider, final SoftwareModuleTypeRepository softwareModuleTypeRepository, final AuditorAware<String> auditorProvider,
final ArtifactManagement artifactManagement, final QuotaManagement quotaManagement, final ArtifactManagement artifactManagement, final QuotaManagement quotaManagement,
final VirtualPropertyReplacer virtualPropertyReplacer, final Database database) { final VirtualPropertyReplacer virtualPropertyReplacer,
final Database database) {
this.entityManager = entityManager; this.entityManager = entityManager;
this.distributionSetRepository = distributionSetRepository; this.distributionSetRepository = distributionSetRepository;
this.softwareModuleRepository = softwareModuleRepository; this.softwareModuleRepository = softwareModuleRepository;
@@ -158,7 +153,7 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
public SoftwareModule create(final SoftwareModuleCreate c) { public SoftwareModule create(final SoftwareModuleCreate c) {
final JpaSoftwareModuleCreate create = (JpaSoftwareModuleCreate) c; final JpaSoftwareModuleCreate create = (JpaSoftwareModuleCreate) c;
final JpaSoftwareModule sm = softwareModuleRepository.save(create.build()); final JpaSoftwareModule sm = softwareModuleRepository.save(AccessController.Operation.CREATE, create.build());
if (create.isEncrypted()) { if (create.isEncrypted()) {
// flush sm creation in order to get an Id // flush sm creation in order to get an Id
entityManager.flush(); entityManager.flush();
@@ -173,25 +168,31 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<SoftwareModule> create(final Collection<SoftwareModuleCreate> swModules) { public List<SoftwareModule> create(final Collection<SoftwareModuleCreate> swModules) {
return swModules.stream().map(this::create).collect(Collectors.toList()); final List<JpaSoftwareModule> modulesToCreate = swModules.stream().map(JpaSoftwareModuleCreate.class::cast)
.map(JpaSoftwareModuleCreate::build).toList();
final List<SoftwareModule> createdModules = Collections
.unmodifiableList(softwareModuleRepository.saveAll(AccessController.Operation.CREATE, modulesToCreate));
if (createdModules.stream().anyMatch(SoftwareModule::isEncrypted)) {
entityManager.flush();
createdModules.stream().filter(SoftwareModule::isEncrypted).map(SoftwareModule::getId)
.forEach(encryptedModuleId -> ArtifactEncryptionService.getInstance()
.addSoftwareModuleEncryptionSecrets(encryptedModuleId));
}
return createdModules;
} }
@Override @Override
public Slice<SoftwareModule> findByType(final Pageable pageable, final long typeId) { public Slice<SoftwareModule> findByType(final Pageable pageable, final long typeId) {
throwExceptionIfSoftwareModuleTypeDoesNotExist(typeId); assertSoftwareModuleTypeExists(typeId);
final List<Specification<JpaSoftwareModule>> specList = Lists.newArrayListWithExpectedSize(2); return JpaManagementHelper.findAllWithoutCountBySpec(
softwareModuleRepository,
specList.add(SoftwareModuleSpecification.equalType(typeId)); pageable,
specList.add(SoftwareModuleSpecification.isDeletedFalse()); List.of(
SoftwareModuleSpecification.equalType(typeId),
return JpaManagementHelper.findAllWithoutCountBySpec(softwareModuleRepository, pageable, specList); SoftwareModuleSpecification.isNotDeleted()));
}
private void throwExceptionIfSoftwareModuleTypeDoesNotExist(final Long typeId) {
if (!softwareModuleTypeRepository.existsById(typeId)) {
throw new EntityNotFoundException(SoftwareModuleType.class, typeId);
}
} }
@Override @Override
@@ -202,19 +203,24 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
@Override @Override
public Optional<SoftwareModule> getByNameAndVersionAndType(final String name, final String version, public Optional<SoftwareModule> getByNameAndVersionAndType(final String name, final String version,
final long typeId) { final long typeId) {
assertSoftwareModuleTypeExists(typeId);
throwExceptionIfSoftwareModuleTypeDoesNotExist(typeId); // TODO AC - Access is restricted. This could have problem with UI when access control is enabled.
// Vaadin UI use this for validation. May need to be called via elevated access
return softwareModuleRepository.findOneByNameAndVersionAndTypeId(name, version, typeId); return JpaManagementHelper
} .findOneBySpec(softwareModuleRepository, List.of(
SoftwareModuleSpecification.likeNameAndVersion(name, version),
private boolean isUnassigned(final Long moduleId) { SoftwareModuleSpecification.equalType(typeId),
return distributionSetRepository.countByModulesId(moduleId) <= 0; SoftwareModuleSpecification.fetchType()))
.map(SoftwareModule.class::cast);
} }
private void deleteGridFsArtifacts(final JpaSoftwareModule swModule) { private void deleteGridFsArtifacts(final JpaSoftwareModule swModule) {
softwareModuleRepository.getAccessController().ifPresent(accessController ->
accessController.assertOperationAllowed(AccessController.Operation.DELETE, swModule));
for (final Artifact localArtifact : swModule.getArtifacts()) { for (final Artifact localArtifact : swModule.getArtifacts()) {
artifactManagement.clearArtifactBinary(localArtifact.getSha1Hash(), swModule.getId()); ((JpaArtifactManagement)artifactManagement)
.clearArtifactBinary(localArtifact.getSha1Hash());
} }
} }
@@ -223,11 +229,10 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void delete(final Collection<Long> ids) { public void delete(final Collection<Long> ids) {
final List<JpaSoftwareModule> swModulesToDelete = softwareModuleRepository.findByIdIn(ids); final List<JpaSoftwareModule> swModulesToDelete = softwareModuleRepository.findAllById(ids);
if (swModulesToDelete.size() < ids.size()) { if (swModulesToDelete.size() < ids.size()) {
throw new EntityNotFoundException(SoftwareModule.class, ids, throw new EntityNotFoundException(SoftwareModule.class, ids,
swModulesToDelete.stream().map(SoftwareModule::getId).collect(Collectors.toList())); swModulesToDelete.stream().map(SoftwareModule::getId).toList());
} }
final Set<Long> assignedModuleIds = new HashSet<>(); final Set<Long> assignedModuleIds = new HashSet<>();
@@ -236,7 +241,9 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
// delete binary data of artifacts // delete binary data of artifacts
deleteGridFsArtifacts(swModule); deleteGridFsArtifacts(swModule);
if (isUnassigned(swModule.getId())) { // execute this count operation without access limitations since we have to
// ensure it's not assigned when deleting it.
if (distributionSetRepository.countByModulesId(swModule.getId()) <= 0) {
softwareModuleRepository.deleteById(swModule.getId()); softwareModuleRepository.deleteById(swModule.getId());
} else { } else {
assignedModuleIds.add(swModule.getId()); assignedModuleIds.add(swModule.getId());
@@ -248,55 +255,61 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
if (auditorProvider != null) { if (auditorProvider != null) {
currentUser = auditorProvider.getCurrentAuditor().orElse(null); currentUser = auditorProvider.getCurrentAuditor().orElse(null);
} }
softwareModuleRepository.deleteSoftwareModule(System.currentTimeMillis(), currentUser,
assignedModuleIds.toArray(new Long[0])); /*
TODO AC - could use single update query as before via entity manager directly (considers tenant!):
maybe it will be possible, via specification when migrate to Spring Boot 3
"UPDATE JpaSoftwareModule b SET b.deleted = 1, b.lastModifiedAt = :lastModifiedAt, b.lastModifiedBy = :lastModifiedBy WHERE b.tenant = :tenant AND b.id IN :ids"
*/
final long timestamp = System.currentTimeMillis();
final List<JpaSoftwareModule> toDelete = softwareModuleRepository.findAll(
AccessController.Operation.DELETE, softwareModuleRepository.byIdsSpec(assignedModuleIds));
for (final JpaSoftwareModule softwareModule : toDelete) {
softwareModule.setDeleted(true);
softwareModule.setLastModifiedAt(timestamp);
softwareModule.setLastModifiedBy(currentUser);
}
softwareModuleRepository.saveAll(toDelete);
} }
} }
@Override @Override
public Slice<SoftwareModule> findAll(final Pageable pageable) { public Slice<SoftwareModule> findAll(final Pageable pageable) {
final List<Specification<JpaSoftwareModule>> specList = new ArrayList<>(2); return JpaManagementHelper.findAllWithoutCountBySpec(softwareModuleRepository, pageable, List.of(
specList.add(SoftwareModuleSpecification.isDeletedFalse()); SoftwareModuleSpecification.isNotDeleted(),
specList.add(SoftwareModuleSpecification.fetchType()); SoftwareModuleSpecification.fetchType()));
return JpaManagementHelper.findAllWithoutCountBySpec(softwareModuleRepository, pageable, specList);
} }
@Override @Override
public long count() { public long count() {
final Specification<JpaSoftwareModule> spec = SoftwareModuleSpecification.isDeletedFalse(); return softwareModuleRepository.count(SoftwareModuleSpecification.isNotDeleted());
return JpaManagementHelper.countBySpec(softwareModuleRepository, Collections.singletonList(spec));
} }
@Override @Override
public Page<SoftwareModule> findByRsql(final Pageable pageable, final String rsqlParam) { public Page<SoftwareModule> findByRsql(final Pageable pageable, final String rsqlParam) {
final List<Specification<JpaSoftwareModule>> specList = Lists.newArrayListWithExpectedSize(2); return JpaManagementHelper.findAllWithCountBySpec(softwareModuleRepository, pageable, List.of(
specList.add(RSQLUtility.buildRsqlSpecification(rsqlParam, SoftwareModuleFields.class, virtualPropertyReplacer, RSQLUtility.buildRsqlSpecification(rsqlParam, SoftwareModuleFields.class, virtualPropertyReplacer,
database)); database),
specList.add(SoftwareModuleSpecification.isDeletedFalse()); SoftwareModuleSpecification.isNotDeleted()));
return JpaManagementHelper.findAllWithCountBySpec(softwareModuleRepository, pageable, specList);
} }
@Override @Override
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
public List<SoftwareModule> get(final Collection<Long> ids) { public List<SoftwareModule> get(final Collection<Long> ids) {
return Collections.unmodifiableList(softwareModuleRepository.findByIdIn(ids)); return Collections.unmodifiableList(softwareModuleRepository.findAllById(ids));
} }
@Override @Override
public Slice<SoftwareModule> findByTextAndType(final Pageable pageable, final String searchText, public Slice<SoftwareModule> findByTextAndType(final Pageable pageable, final String searchText,
final Long typeId) { final Long typeId) {
final List<Specification<JpaSoftwareModule>> specList = new ArrayList<>(4); final List<Specification<JpaSoftwareModule>> specList = new ArrayList<>(3);
specList.add(SoftwareModuleSpecification.isDeletedFalse()); specList.add(SoftwareModuleSpecification.isNotDeleted());
if (!StringUtils.isEmpty(searchText)) { if (!ObjectUtils.isEmpty(searchText)) {
specList.add(buildSmSearchQuerySpec(searchText)); specList.add(buildSmSearchQuerySpec(searchText));
} }
if (null != typeId) { if (null != typeId) {
throwExceptionIfSoftwareModuleTypeDoesNotExist(typeId); assertSoftwareModuleTypeExists(typeId);
specList.add(SoftwareModuleSpecification.equalType(typeId)); specList.add(SoftwareModuleSpecification.equalType(typeId));
} }
@@ -312,13 +325,17 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
smFilterNameAndVersionEntries[1]); smFilterNameAndVersionEntries[1]);
} }
/**
* @deprecated Used only in UI which is to be removed
*/
@Deprecated(forRemoval = true)
@Override @Override
// In the interface org.springframework.data.domain.Pageable.getSort the // In the interface org.springframework.data.domain.Pageable.getSort the
// return value is not guaranteed to be non-null, therefore a null check is // return value is not guaranteed to be non-null, therefore a null check is
// necessary otherwise we rely on the implementation but this could change. // necessary otherwise we rely on the implementation but this could change.
@SuppressWarnings({ "squid:S2583", "squid:S2589" }) @SuppressWarnings({ "squid:S2583", "squid:S2589" })
public Slice<AssignedSoftwareModule> findAllOrderBySetAssignmentAndModuleNameAscModuleVersionAsc( public Slice<AssignedSoftwareModule> findAllOrderBySetAssignmentAndModuleNameAscModuleVersionAsc(
final Pageable pageable, final long dsId, final String searchText, final Long smTypeId) { final Pageable pageable, final long orderByDistributionSetId, final String searchText, final Long typeId) {
final CriteriaBuilder cb = entityManager.getCriteriaBuilder(); final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaQuery<Tuple> query = cb.createTupleQuery(); final CriteriaQuery<Tuple> query = cb.createTupleQuery();
final Root<JpaSoftwareModule> smRoot = query.from(JpaSoftwareModule.class); final Root<JpaSoftwareModule> smRoot = query.from(JpaSoftwareModule.class);
@@ -327,11 +344,11 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
.join(JpaSoftwareModule_.assignedTo, JoinType.LEFT); .join(JpaSoftwareModule_.assignedTo, JoinType.LEFT);
final Expression<Integer> assignedCaseMax = cb.max( final Expression<Integer> assignedCaseMax = cb.max(
cb.<Long, Integer> selectCase(assignedDsList.get(JpaDistributionSet_.id)).when(dsId, 1).otherwise(0)); cb.<Long, Integer> selectCase(assignedDsList.get(JpaDistributionSet_.id)).when(orderByDistributionSetId, 1).otherwise(0));
query.multiselect(smRoot.alias("sm"), assignedCaseMax.alias("assigned")); query.multiselect(smRoot.alias("sm"), assignedCaseMax.alias("assigned"));
final Predicate[] specPredicate = specificationsToPredicate(buildSpecificationList(searchText, smTypeId), final Predicate[] specPredicate = specificationsToPredicate(buildSpecificationList(searchText, typeId),
smRoot, query, cb); smRoot, query, cb);
if (specPredicate.length > 0) { if (specPredicate.length > 0) {
@@ -358,25 +375,41 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
final List<AssignedSoftwareModule> resultList = new ArrayList<>(); final List<AssignedSoftwareModule> resultList = new ArrayList<>();
smWithAssignedFlagList.forEach(smWithAssignedFlag -> resultList smWithAssignedFlagList.forEach(smWithAssignedFlag -> {
.add(new AssignedSoftwareModule(smWithAssignedFlag.get("sm", JpaSoftwareModule.class), final JpaSoftwareModule softwareModule = smWithAssignedFlag.get("sm", JpaSoftwareModule.class);
smWithAssignedFlag.get("assigned", Number.class).longValue() == 1))); try {
// check read access
if (softwareModuleRepository.getAccessController().map(accessController -> {
try {
accessController.assertOperationAllowed(AccessController.Operation.READ, softwareModule);
return true;
} catch (final InsufficientPermissionException e) {
return false;
}
}).orElse(true)) {
resultList.add(new AssignedSoftwareModule(softwareModule,
smWithAssignedFlag.get("assigned", Number.class).longValue() == 1));
}
} catch (final InsufficientPermissionException e) {
// skip the entry
}
});
return new SliceImpl<>(Collections.unmodifiableList(resultList), pageable, hasNext); return new SliceImpl<>(Collections.unmodifiableList(resultList), pageable, hasNext);
} }
private List<Specification<JpaSoftwareModule>> buildSpecificationList(final String searchText, final Long typeId) { private List<Specification<JpaSoftwareModule>> buildSpecificationList(final String searchText, final Long typeId) {
final List<Specification<JpaSoftwareModule>> specList = Lists.newArrayListWithExpectedSize(3); final List<Specification<JpaSoftwareModule>> specList = new ArrayList<>(3);
if (!StringUtils.isEmpty(searchText)) { if (!ObjectUtils.isEmpty(searchText)) {
specList.add(buildSmSearchQuerySpec(searchText)); specList.add(buildSmSearchQuerySpec(searchText));
} }
if (typeId != null) { if (typeId != null) {
throwExceptionIfSoftwareModuleTypeDoesNotExist(typeId); assertSoftwareModuleTypeExists(typeId);
specList.add(SoftwareModuleSpecification.equalType(typeId)); specList.add(SoftwareModuleSpecification.equalType(typeId));
} }
specList.add(SoftwareModuleSpecification.isDeletedFalse()); specList.add(SoftwareModuleSpecification.isNotDeleted());
return specList; return specList;
} }
@@ -388,19 +421,25 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
Arrays.stream(additionalPredicates)).toArray(Predicate[]::new); Arrays.stream(additionalPredicates)).toArray(Predicate[]::new);
} }
/**
* No access control applied
*
* @deprecated Used only in UI which is to be removed
*/
@Deprecated(forRemoval = true)
@Override @Override
public long countByTextAndType(final String searchText, final Long typeId) { public long countByTextAndType(final String searchText, final Long typeId) {
final List<Specification<JpaSoftwareModule>> specList = new ArrayList<>(3); final List<Specification<JpaSoftwareModule>> specList = new ArrayList<>(3);
Specification<JpaSoftwareModule> spec = SoftwareModuleSpecification.isDeletedFalse(); Specification<JpaSoftwareModule> spec = SoftwareModuleSpecification.isNotDeleted();
specList.add(spec); specList.add(spec);
if (!StringUtils.isEmpty(searchText)) { if (!ObjectUtils.isEmpty(searchText)) {
specList.add(buildSmSearchQuerySpec(searchText)); specList.add(buildSmSearchQuerySpec(searchText));
} }
if (null != typeId) { if (null != typeId) {
throwExceptionIfSoftwareModuleTypeDoesNotExist(typeId); assertSoftwareModuleTypeExists(typeId);
spec = SoftwareModuleSpecification.equalType(typeId); spec = SoftwareModuleSpecification.equalType(typeId);
specList.add(spec); specList.add(spec);
@@ -410,21 +449,18 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
} }
@Override @Override
public Page<SoftwareModule> findByAssignedTo(final Pageable pageable, final long setId) { public Page<SoftwareModule> findByAssignedTo(final Pageable pageable, final long distributionSetId) {
if (!distributionSetRepository.existsById(setId)) { assertDistributionSetExists(distributionSetId);
throw new EntityNotFoundException(DistributionSet.class, setId);
}
return softwareModuleRepository.findByAssignedToId(pageable, setId); return JpaManagementHelper.findAllWithCountBySpec(softwareModuleRepository, pageable,
Collections.singletonList(SoftwareModuleSpecification.byAssignedToDs(distributionSetId)));
} }
@Override @Override
public long countByAssignedTo(final long setId) { public long countByAssignedTo(final long distributionSetId) {
if (!distributionSetRepository.existsById(setId)) { assertDistributionSetExists(distributionSetId);
throw new EntityNotFoundException(DistributionSet.class, setId);
}
return softwareModuleRepository.countByAssignedToId(setId); return softwareModuleRepository.count(SoftwareModuleSpecification.byAssignedToDs(distributionSetId));
} }
@Override @Override
@@ -432,12 +468,15 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public SoftwareModuleMetadata createMetaData(final SoftwareModuleMetadataCreate c) { public SoftwareModuleMetadata createMetaData(final SoftwareModuleMetadataCreate c) {
final JpaSoftwareModuleMetadataCreate create = (JpaSoftwareModuleMetadataCreate) c; final JpaSoftwareModuleMetadataCreate create = (JpaSoftwareModuleMetadataCreate) c;
final Long moduleId = create.getSoftwareModuleId(); final Long id = create.getSoftwareModuleId();
assertSoftwareModuleExists(moduleId);
assertMetaDataQuota(moduleId, 1);
assertSoftwareModuleExists(id);
assertMetaDataQuota(id, 1);
// touch to update revision and last modified timestamp
JpaManagementHelper.touch(entityManager, softwareModuleRepository, (JpaSoftwareModule) get(id)
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id)));
return saveMetadata(create); return saveMetadata(create);
} }
@@ -446,31 +485,30 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<SoftwareModuleMetadata> createMetaData(final Collection<SoftwareModuleMetadataCreate> create) { public List<SoftwareModuleMetadata> createMetaData(final Collection<SoftwareModuleMetadataCreate> create) {
if (!create.isEmpty()) { if (!create.isEmpty()) {
// check if all metadata entries refer to the same software module // check if all metadata entries refer to the same software module
final Long moduleId = ((JpaSoftwareModuleMetadataCreate) create.iterator().next()).getSoftwareModuleId(); final Long id = ((JpaSoftwareModuleMetadataCreate) create.iterator().next()).getSoftwareModuleId();
if (createJpaMetadataCreateStream(create).allMatch(c -> moduleId.equals(c.getSoftwareModuleId()))) { if (createJpaMetadataCreateStream(create).allMatch(c -> id.equals(c.getSoftwareModuleId()))) {
assertSoftwareModuleExists(id);
assertSoftwareModuleExists(moduleId); assertMetaDataQuota(id, create.size());
assertMetaDataQuota(moduleId, create.size());
// touch to update revision and last modified timestamp
JpaManagementHelper.touch(entityManager, softwareModuleRepository, (JpaSoftwareModule) get(id)
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id)));
return createJpaMetadataCreateStream(create).map(this::saveMetadata).collect(Collectors.toList()); return createJpaMetadataCreateStream(create).map(this::saveMetadata).collect(Collectors.toList());
} else { } else {
// group by software module id to minimize database access // group by software module id to minimize database access
final Map<Long, List<JpaSoftwareModuleMetadataCreate>> groups = createJpaMetadataCreateStream(create) final Map<Long, List<JpaSoftwareModuleMetadataCreate>> groups = createJpaMetadataCreateStream(create)
.collect(Collectors.groupingBy(JpaSoftwareModuleMetadataCreate::getSoftwareModuleId)); .collect(Collectors.groupingBy(JpaSoftwareModuleMetadataCreate::getSoftwareModuleId));
return groups.entrySet().stream().flatMap(e -> { return groups.entrySet().stream().flatMap(e -> {
final Long id = e.getKey();
final List<JpaSoftwareModuleMetadataCreate> group = e.getValue(); final List<JpaSoftwareModuleMetadataCreate> group = e.getValue();
assertSoftwareModuleExists(id); assertSoftwareModuleExists(id);
assertMetaDataQuota(id, group.size()); assertMetaDataQuota(e.getKey(), group.size());
// touch to update revision and last modified timestamp
JpaManagementHelper.touch(entityManager, softwareModuleRepository, (JpaSoftwareModule) get(id)
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id)));
return group.stream().map(this::saveMetadata); return group.stream().map(this::saveMetadata);
}).collect(Collectors.toList()); }).collect(Collectors.toList());
} }
@@ -489,29 +527,24 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
return softwareModuleMetadataRepository.save(create.build()); return softwareModuleMetadataRepository.save(create.build());
} }
private void assertSoftwareModuleMetadataDoesNotExist(final Long moduleId, private void assertSoftwareModuleMetadataDoesNotExist(final Long id,
final JpaSoftwareModuleMetadataCreate md) { final JpaSoftwareModuleMetadataCreate md) {
if (softwareModuleMetadataRepository.existsById(new SwMetadataCompositeKey(moduleId, md.getKey()))) { if (softwareModuleMetadataRepository.existsById(new SwMetadataCompositeKey(id, md.getKey()))) {
throwMetadataKeyAlreadyExists(md.getKey()); throw new EntityAlreadyExistsException("Metadata entry with key '" + md.getKey() + "' already exists!");
} }
} }
private void assertSoftwareModuleExists(final Long moduleId) {
JpaManagementHelper.touch(entityManager, softwareModuleRepository, (JpaSoftwareModule) get(moduleId)
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, moduleId)));
}
/** /**
* Asserts the meta data quota for the software module with the given ID. * Asserts the meta data quota for the software module with the given ID.
* *
* @param moduleId * @param id
* The software module ID. * The software module ID.
* @param requested * @param requested
* Number of meta data entries to be created. * Number of meta data entries to be created.
*/ */
private void assertMetaDataQuota(final Long moduleId, final int requested) { private void assertMetaDataQuota(final Long id, final int requested) {
final int maxMetaData = quotaManagement.getMaxMetaDataEntriesPerSoftwareModule(); final int maxMetaData = quotaManagement.getMaxMetaDataEntriesPerSoftwareModule();
QuotaHelper.assertAssignmentQuota(moduleId, requested, maxMetaData, SoftwareModuleMetadata.class, QuotaHelper.assertAssignmentQuota(id, requested, maxMetaData, SoftwareModuleMetadata.class,
SoftwareModule.class, softwareModuleMetadataRepository::countBySoftwareModuleId); SoftwareModule.class, softwareModuleMetadataRepository::countBySoftwareModuleId);
} }
@@ -540,70 +573,55 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
@Transactional @Transactional
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteMetaData(final long moduleId, final String key) { public void deleteMetaData(final long id, final String key) {
final JpaSoftwareModuleMetadata metadata = (JpaSoftwareModuleMetadata) getMetaDataBySoftwareModuleId(moduleId, final JpaSoftwareModuleMetadata metadata = (JpaSoftwareModuleMetadata) getMetaDataBySoftwareModuleId(id,
key).orElseThrow(() -> new EntityNotFoundException(SoftwareModuleMetadata.class, moduleId, key)); key).orElseThrow(() -> new EntityNotFoundException(SoftwareModuleMetadata.class, id, key));
JpaManagementHelper.touch(entityManager, softwareModuleRepository, JpaManagementHelper.touch(entityManager, softwareModuleRepository,
(JpaSoftwareModule) metadata.getSoftwareModule()); (JpaSoftwareModule) metadata.getSoftwareModule());
softwareModuleMetadataRepository.deleteById(metadata.getId()); softwareModuleMetadataRepository.deleteById(metadata.getId());
} }
private void throwExceptionIfSoftwareModuleDoesNotExist(final Long swId) {
if (!softwareModuleRepository.existsById(swId)) {
throw new EntityNotFoundException(SoftwareModule.class, swId);
}
}
@Override @Override
public Page<SoftwareModuleMetadata> findMetaDataByRsql(final Pageable pageable, final long softwareModuleId, public Page<SoftwareModuleMetadata> findMetaDataByRsql(final Pageable pageable, final long id,
final String rsqlParam) { final String rsqlParam) {
throwExceptionIfSoftwareModuleDoesNotExist(softwareModuleId); assertSoftwareModuleExists(id);
final List<Specification<JpaSoftwareModuleMetadata>> specList = Arrays final List<Specification<JpaSoftwareModuleMetadata>> specList = Arrays
.asList(RSQLUtility.buildRsqlSpecification(rsqlParam, SoftwareModuleMetadataFields.class, .asList(RSQLUtility.buildRsqlSpecification(rsqlParam, SoftwareModuleMetadataFields.class,
virtualPropertyReplacer, database), bySmIdSpec(softwareModuleId)); virtualPropertyReplacer, database), metadataBySoftwareModuleIdSpec(id));
return JpaManagementHelper.findAllWithCountBySpec(softwareModuleMetadataRepository, pageable, specList); return JpaManagementHelper.findAllWithCountBySpec(softwareModuleMetadataRepository, pageable, specList);
} }
private static Specification<JpaSoftwareModuleMetadata> bySmIdSpec(final long smId) {
return (root, query, cb) -> cb
.equal(root.get(JpaSoftwareModuleMetadata_.softwareModule).get(JpaSoftwareModule_.id), smId);
}
@Override @Override
public Page<SoftwareModuleMetadata> findMetaDataBySoftwareModuleId(final Pageable pageable, final long swId) { public Page<SoftwareModuleMetadata> findMetaDataBySoftwareModuleId(final Pageable pageable, final long id) {
throwExceptionIfSoftwareModuleDoesNotExist(swId); assertSoftwareModuleExists(id);
return JpaManagementHelper.findAllWithCountBySpec(softwareModuleMetadataRepository, pageable, return JpaManagementHelper.findAllWithCountBySpec(softwareModuleMetadataRepository, pageable,
Collections.singletonList(bySmIdSpec(swId))); Collections.singletonList(metadataBySoftwareModuleIdSpec(id)));
} }
@Override @Override
public long countMetaDataBySoftwareModuleId(final long moduleId) { public long countMetaDataBySoftwareModuleId(final long id) {
throwExceptionIfSoftwareModuleDoesNotExist(moduleId); assertSoftwareModuleExists(id);
return softwareModuleMetadataRepository.countBySoftwareModuleId(moduleId); return softwareModuleMetadataRepository.countBySoftwareModuleId(id);
} }
@Override @Override
public Optional<SoftwareModuleMetadata> getMetaDataBySoftwareModuleId(final long moduleId, final String key) { public Optional<SoftwareModuleMetadata> getMetaDataBySoftwareModuleId(final long id, final String key) {
throwExceptionIfSoftwareModuleDoesNotExist(moduleId); assertSoftwareModuleExists(id);
return softwareModuleMetadataRepository.findById(new SwMetadataCompositeKey(moduleId, key)) return softwareModuleMetadataRepository.findById(new SwMetadataCompositeKey(id, key))
.map(SoftwareModuleMetadata.class::cast); .map(SoftwareModuleMetadata.class::cast);
} }
private static void throwMetadataKeyAlreadyExists(final String metadataKey) {
throw new EntityAlreadyExistsException("Metadata entry with key '" + metadataKey + "' already exists");
}
@Override @Override
@Transactional @Transactional
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void delete(final long moduleId) { public void delete(final long id) {
delete(Arrays.asList(moduleId)); delete(List.of(id));
} }
@Override @Override
@@ -613,11 +631,33 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
@Override @Override
public Page<SoftwareModuleMetadata> findMetaDataBySoftwareModuleIdAndTargetVisible(final Pageable pageable, public Page<SoftwareModuleMetadata> findMetaDataBySoftwareModuleIdAndTargetVisible(final Pageable pageable,
final long moduleId) { final long id) {
throwExceptionIfSoftwareModuleDoesNotExist(moduleId); assertSoftwareModuleExists(id);
return JpaManagementHelper.convertPage(softwareModuleMetadataRepository.findBySoftwareModuleIdAndTargetVisible( return JpaManagementHelper.convertPage(softwareModuleMetadataRepository.findBySoftwareModuleIdAndTargetVisible(
PageRequest.of(0, RepositoryConstants.MAX_META_DATA_COUNT), moduleId, true), pageable); PageRequest.of(0, RepositoryConstants.MAX_META_DATA_COUNT), id, true), pageable);
} }
private void assertSoftwareModuleExists(final Long id) {
if (!softwareModuleRepository.existsById(id)) {
throw new EntityNotFoundException(SoftwareModule.class, id);
}
}
private void assertSoftwareModuleTypeExists(final Long typeId) {
if (!softwareModuleTypeRepository.existsById(typeId)) {
throw new EntityNotFoundException(SoftwareModuleType.class, typeId);
}
}
private void assertDistributionSetExists(final long distributionSetId) {
if (!distributionSetRepository.existsById(distributionSetId)) {
throw new EntityNotFoundException(DistributionSet.class, distributionSetId);
}
}
private static Specification<JpaSoftwareModuleMetadata> metadataBySoftwareModuleIdSpec(final long id) {
return (root, query, cb) -> cb
.equal(root.get(JpaSoftwareModuleMetadata_.softwareModule).get(JpaSoftwareModule_.id), id);
}
} }

View File

@@ -7,14 +7,12 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.management;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.repository.SoftwareModuleTypeFields; import org.eclipse.hawkbit.repository.SoftwareModuleTypeFields;
import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement; import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement;
@@ -22,9 +20,14 @@ import org.eclipse.hawkbit.repository.builder.GenericSoftwareModuleTypeUpdate;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleTypeCreate; import org.eclipse.hawkbit.repository.builder.SoftwareModuleTypeCreate;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleTypeUpdate; import org.eclipse.hawkbit.repository.builder.SoftwareModuleTypeUpdate;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.eclipse.hawkbit.repository.jpa.builder.JpaSoftwareModuleTypeCreate; import org.eclipse.hawkbit.repository.jpa.builder.JpaSoftwareModuleTypeCreate;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants; import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType; import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetTypeRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleTypeRepository;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility; import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.jpa.specifications.SoftwareModuleTypeSpecification; import org.eclipse.hawkbit.repository.jpa.specifications.SoftwareModuleTypeSpecification;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
@@ -48,19 +51,16 @@ import org.springframework.validation.annotation.Validated;
public class JpaSoftwareModuleTypeManagement implements SoftwareModuleTypeManagement { public class JpaSoftwareModuleTypeManagement implements SoftwareModuleTypeManagement {
private final DistributionSetTypeRepository distributionSetTypeRepository; private final DistributionSetTypeRepository distributionSetTypeRepository;
private final SoftwareModuleTypeRepository softwareModuleTypeRepository; private final SoftwareModuleTypeRepository softwareModuleTypeRepository;
private final VirtualPropertyReplacer virtualPropertyReplacer; private final VirtualPropertyReplacer virtualPropertyReplacer;
private final SoftwareModuleRepository softwareModuleRepository; private final SoftwareModuleRepository softwareModuleRepository;
private final Database database; private final Database database;
public JpaSoftwareModuleTypeManagement(final DistributionSetTypeRepository distributionSetTypeRepository, public JpaSoftwareModuleTypeManagement(final DistributionSetTypeRepository distributionSetTypeRepository,
final SoftwareModuleTypeRepository softwareModuleTypeRepository, final SoftwareModuleTypeRepository softwareModuleTypeRepository,
final VirtualPropertyReplacer virtualPropertyReplacer, final VirtualPropertyReplacer virtualPropertyReplacer,
final SoftwareModuleRepository softwareModuleRepository, final Database database) { final SoftwareModuleRepository softwareModuleRepository,
final Database database) {
this.distributionSetTypeRepository = distributionSetTypeRepository; this.distributionSetTypeRepository = distributionSetTypeRepository;
this.softwareModuleTypeRepository = softwareModuleTypeRepository; this.softwareModuleTypeRepository = softwareModuleTypeRepository;
this.virtualPropertyReplacer = virtualPropertyReplacer; this.virtualPropertyReplacer = virtualPropertyReplacer;
@@ -86,23 +86,22 @@ public class JpaSoftwareModuleTypeManagement implements SoftwareModuleTypeManage
@Override @Override
public Page<SoftwareModuleType> findByRsql(final Pageable pageable, final String rsqlParam) { public Page<SoftwareModuleType> findByRsql(final Pageable pageable, final String rsqlParam) {
return JpaManagementHelper return JpaManagementHelper.findAllWithCountBySpec(softwareModuleTypeRepository, pageable,
.findAllWithCountBySpec(softwareModuleTypeRepository, pageable, List.of(
Arrays.asList(
RSQLUtility.buildRsqlSpecification(rsqlParam, SoftwareModuleTypeFields.class, RSQLUtility.buildRsqlSpecification(rsqlParam, SoftwareModuleTypeFields.class,
virtualPropertyReplacer, database), virtualPropertyReplacer, database),
SoftwareModuleTypeSpecification.isDeleted(false))); SoftwareModuleTypeSpecification.isNotDeleted()));
} }
@Override @Override
public Slice<SoftwareModuleType> findAll(final Pageable pageable) { public Slice<SoftwareModuleType> findAll(final Pageable pageable) {
return JpaManagementHelper.findAllWithoutCountBySpec(softwareModuleTypeRepository, pageable, return JpaManagementHelper.findAllWithoutCountBySpec(softwareModuleTypeRepository, pageable,
Collections.singletonList(SoftwareModuleTypeSpecification.isDeleted(false))); List.of(SoftwareModuleTypeSpecification.isNotDeleted()));
} }
@Override @Override
public long count() { public long count() {
return softwareModuleTypeRepository.countByDeleted(false); return softwareModuleTypeRepository.count(SoftwareModuleTypeSpecification.isNotDeleted());
} }
@Override @Override
@@ -122,32 +121,29 @@ public class JpaSoftwareModuleTypeManagement implements SoftwareModuleTypeManage
public SoftwareModuleType create(final SoftwareModuleTypeCreate c) { public SoftwareModuleType create(final SoftwareModuleTypeCreate c) {
final JpaSoftwareModuleTypeCreate create = (JpaSoftwareModuleTypeCreate) c; final JpaSoftwareModuleTypeCreate create = (JpaSoftwareModuleTypeCreate) c;
return softwareModuleTypeRepository.save(create.build()); return softwareModuleTypeRepository.save(AccessController.Operation.CREATE, create.build());
} }
@Override @Override
@Transactional @Transactional
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void delete(final long typeId) { public void delete(final long id) {
final JpaSoftwareModuleType toDelete = softwareModuleTypeRepository.findById(typeId) final JpaSoftwareModuleType toDelete = softwareModuleTypeRepository.findById(id)
.orElseThrow(() -> new EntityNotFoundException(SoftwareModuleType.class, typeId)); .orElseThrow(() -> new EntityNotFoundException(SoftwareModuleType.class, id));
if (softwareModuleRepository.countByType(toDelete) > 0 delete(toDelete);
|| distributionSetTypeRepository.countByElementsSmType(toDelete) > 0) {
toDelete.setDeleted(true);
softwareModuleTypeRepository.save(toDelete);
} else {
softwareModuleTypeRepository.delete(toDelete);
}
} }
@Override @Override
@Transactional @Transactional
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<SoftwareModuleType> create(final Collection<SoftwareModuleTypeCreate> creates) { public List<SoftwareModuleType> create(final Collection<SoftwareModuleTypeCreate> c) {
return creates.stream().map(this::create).collect(Collectors.toList()); final List<JpaSoftwareModuleType> creates = c.stream().map(JpaSoftwareModuleTypeCreate.class::cast)
.map(JpaSoftwareModuleTypeCreate::build).toList();
return Collections.unmodifiableList(
softwareModuleTypeRepository.saveAll(AccessController.Operation.CREATE, creates));
} }
@Override @Override
@@ -155,14 +151,9 @@ public class JpaSoftwareModuleTypeManagement implements SoftwareModuleTypeManage
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void delete(final Collection<Long> ids) { public void delete(final Collection<Long> ids) {
final List<JpaSoftwareModuleType> setsFound = softwareModuleTypeRepository.findAllById(ids); softwareModuleTypeRepository
.findAll(AccessController.Operation.DELETE, softwareModuleTypeRepository.byIdsSpec(ids))
if (setsFound.size() < ids.size()) { .forEach(this::delete);
throw new EntityNotFoundException(SoftwareModuleType.class, ids,
setsFound.stream().map(SoftwareModuleType::getId).collect(Collectors.toList()));
}
softwareModuleTypeRepository.deleteAll(setsFound);
} }
@Override @Override
@@ -172,7 +163,7 @@ public class JpaSoftwareModuleTypeManagement implements SoftwareModuleTypeManage
@Override @Override
public Optional<SoftwareModuleType> get(final long id) { public Optional<SoftwareModuleType> get(final long id) {
return softwareModuleTypeRepository.findById(id).map(smt -> (SoftwareModuleType) smt); return softwareModuleTypeRepository.findById(id).map(SoftwareModuleType.class::cast);
} }
@Override @Override
@@ -180,4 +171,13 @@ public class JpaSoftwareModuleTypeManagement implements SoftwareModuleTypeManage
return softwareModuleTypeRepository.existsById(id); return softwareModuleTypeRepository.existsById(id);
} }
private void delete(JpaSoftwareModuleType toDelete) {
if (softwareModuleRepository.countByType(toDelete) > 0
|| distributionSetTypeRepository.countByElementsSmType(toDelete) > 0) {
toDelete.setDeleted(true);
softwareModuleTypeRepository.save(AccessController.Operation.DELETE, toDelete);
} else {
softwareModuleTypeRepository.delete(toDelete);
}
}
} }

View File

@@ -7,7 +7,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.management;
import java.util.Collections; import java.util.Collections;
import java.util.function.Consumer; import java.util.function.Consumer;
@@ -21,11 +21,25 @@ import org.eclipse.hawkbit.repository.RolloutStatusCache;
import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.TenantStatsManagement; import org.eclipse.hawkbit.repository.TenantStatsManagement;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.CurrentTenantCacheKeyGenerator;
import org.eclipse.hawkbit.repository.jpa.SystemManagementCacheKeyGenerator;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants; import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.configuration.MultiTenantJpaTransactionManager; import org.eclipse.hawkbit.repository.jpa.configuration.MultiTenantJpaTransactionManager;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType; import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
import org.eclipse.hawkbit.repository.jpa.model.JpaTenantMetaData; import org.eclipse.hawkbit.repository.jpa.model.JpaTenantMetaData;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetRepository;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetTagRepository;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetTypeRepository;
import org.eclipse.hawkbit.repository.jpa.repository.RolloutRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleTypeRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetFilterQueryRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetTagRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetTypeRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TenantConfigurationRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TenantMetaDataRepository;
import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper; import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper;
import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.repository.model.SoftwareModuleType;

View File

@@ -7,7 +7,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.management;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@@ -22,6 +22,7 @@ import org.eclipse.hawkbit.repository.TargetFilterQueryFields;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement; import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.TargetManagement; import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.ContextAware;
import org.eclipse.hawkbit.repository.builder.AutoAssignDistributionSetUpdate; import org.eclipse.hawkbit.repository.builder.AutoAssignDistributionSetUpdate;
import org.eclipse.hawkbit.repository.builder.GenericTargetFilterQueryUpdate; import org.eclipse.hawkbit.repository.builder.GenericTargetFilterQueryUpdate;
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryCreate; import org.eclipse.hawkbit.repository.builder.TargetFilterQueryCreate;
@@ -29,10 +30,13 @@ import org.eclipse.hawkbit.repository.builder.TargetFilterQueryUpdate;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.InvalidAutoAssignActionTypeException; import org.eclipse.hawkbit.repository.exception.InvalidAutoAssignActionTypeException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException; import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetFilterQueryCreate; import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetFilterQueryCreate;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants; import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery; import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery;
import org.eclipse.hawkbit.repository.jpa.repository.TargetFilterQueryRepository;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility; import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetFilterQuerySpecification; import org.eclipse.hawkbit.repository.jpa.specifications.TargetFilterQuerySpecification;
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper; import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
@@ -43,7 +47,6 @@ import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery; import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer; import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.hawkbit.utils.TenantConfigHelper; import org.eclipse.hawkbit.utils.TenantConfigHelper;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -56,7 +59,7 @@ import org.springframework.orm.jpa.vendor.Database;
import org.springframework.retry.annotation.Backoff; import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable; import org.springframework.retry.annotation.Retryable;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils; import org.springframework.util.ObjectUtils;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
@@ -82,15 +85,15 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
private final QuotaManagement quotaManagement; private final QuotaManagement quotaManagement;
private final TenantConfigurationManagement tenantConfigurationManagement; private final TenantConfigurationManagement tenantConfigurationManagement;
private final SystemSecurityContext systemSecurityContext; private final SystemSecurityContext systemSecurityContext;
private final TenantAware tenantAware; private final ContextAware contextAware;
private final Database database; private final Database database;
JpaTargetFilterQueryManagement(final TargetFilterQueryRepository targetFilterQueryRepository, public JpaTargetFilterQueryManagement(final TargetFilterQueryRepository targetFilterQueryRepository,
final TargetManagement targetManagement, final VirtualPropertyReplacer virtualPropertyReplacer, final TargetManagement targetManagement, final VirtualPropertyReplacer virtualPropertyReplacer,
final DistributionSetManagement distributionSetManagement, final QuotaManagement quotaManagement, final DistributionSetManagement distributionSetManagement, final QuotaManagement quotaManagement,
final Database database, final TenantConfigurationManagement tenantConfigurationManagement, final Database database, final TenantConfigurationManagement tenantConfigurationManagement,
final SystemSecurityContext systemSecurityContext, final TenantAware tenantAware) { final SystemSecurityContext systemSecurityContext, final ContextAware contextAware) {
this.targetFilterQueryRepository = targetFilterQueryRepository; this.targetFilterQueryRepository = targetFilterQueryRepository;
this.targetManagement = targetManagement; this.targetManagement = targetManagement;
this.virtualPropertyReplacer = virtualPropertyReplacer; this.virtualPropertyReplacer = virtualPropertyReplacer;
@@ -99,7 +102,7 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
this.database = database; this.database = database;
this.tenantConfigurationManagement = tenantConfigurationManagement; this.tenantConfigurationManagement = tenantConfigurationManagement;
this.systemSecurityContext = systemSecurityContext; this.systemSecurityContext = systemSecurityContext;
this.tenantAware = tenantAware; this.contextAware = contextAware;
} }
@Override @Override
@@ -122,7 +125,7 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
}); });
}); });
return targetFilterQueryRepository.save(create.build()); return targetFilterQueryRepository.save(AccessController.Operation.CREATE, create.build());
} }
@Override @Override
@@ -154,7 +157,7 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
@Override @Override
public Slice<TargetFilterQuery> findByName(final Pageable pageable, final String name) { public Slice<TargetFilterQuery> findByName(final Pageable pageable, final String name) {
if (StringUtils.isEmpty(name)) { if (ObjectUtils.isEmpty(name)) {
return findAll(pageable); return findAll(pageable);
} }
@@ -164,7 +167,7 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
@Override @Override
public long countByName(final String name) { public long countByName(final String name) {
if (StringUtils.isEmpty(name)) { if (ObjectUtils.isEmpty(name)) {
return count(); return count();
} }
@@ -174,7 +177,7 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
@Override @Override
public Page<TargetFilterQuery> findByRsql(final Pageable pageable, final String rsqlFilter) { public Page<TargetFilterQuery> findByRsql(final Pageable pageable, final String rsqlFilter) {
final List<Specification<JpaTargetFilterQuery>> specList = !StringUtils.isEmpty(rsqlFilter) final List<Specification<JpaTargetFilterQuery>> specList = !ObjectUtils.isEmpty(rsqlFilter)
? Collections.singletonList(RSQLUtility.buildRsqlSpecification(rsqlFilter, ? Collections.singletonList(RSQLUtility.buildRsqlSpecification(rsqlFilter,
TargetFilterQueryFields.class, virtualPropertyReplacer, database)) TargetFilterQueryFields.class, virtualPropertyReplacer, database))
: Collections.emptyList(); : Collections.emptyList();
@@ -184,7 +187,7 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
@Override @Override
public Slice<TargetFilterQuery> findByQuery(final Pageable pageable, final String query) { public Slice<TargetFilterQuery> findByQuery(final Pageable pageable, final String query) {
final List<Specification<JpaTargetFilterQuery>> specList = !StringUtils.isEmpty(query) final List<Specification<JpaTargetFilterQuery>> specList = !ObjectUtils.isEmpty(query)
? Collections.singletonList(TargetFilterQuerySpecification.equalsQuery(query)) ? Collections.singletonList(TargetFilterQuerySpecification.equalsQuery(query))
: Collections.emptyList(); : Collections.emptyList();
@@ -207,7 +210,7 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
final List<Specification<JpaTargetFilterQuery>> specList = Lists.newArrayListWithExpectedSize(2); final List<Specification<JpaTargetFilterQuery>> specList = Lists.newArrayListWithExpectedSize(2);
specList.add(TargetFilterQuerySpecification.byAutoAssignDS(distributionSet)); specList.add(TargetFilterQuerySpecification.byAutoAssignDS(distributionSet));
if (!StringUtils.isEmpty(rsqlFilter)) { if (!ObjectUtils.isEmpty(rsqlFilter)) {
specList.add(RSQLUtility.buildRsqlSpecification(rsqlFilter, TargetFilterQueryFields.class, specList.add(RSQLUtility.buildRsqlSpecification(rsqlFilter, TargetFilterQueryFields.class,
virtualPropertyReplacer, database)); virtualPropertyReplacer, database));
} }
@@ -228,7 +231,7 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
@Override @Override
public Optional<TargetFilterQuery> get(final long targetFilterQueryId) { public Optional<TargetFilterQuery> get(final long targetFilterQueryId) {
return targetFilterQueryRepository.findById(targetFilterQueryId).map(tfq -> (TargetFilterQuery) tfq); return targetFilterQueryRepository.findById(targetFilterQueryId).map(TargetFilterQuery.class::cast);
} }
@Override @Override
@@ -262,7 +265,9 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
public TargetFilterQuery updateAutoAssignDS(final AutoAssignDistributionSetUpdate update) { public TargetFilterQuery updateAutoAssignDS(final AutoAssignDistributionSetUpdate update) {
final JpaTargetFilterQuery targetFilterQuery = findTargetFilterQueryOrThrowExceptionIfNotFound( final JpaTargetFilterQuery targetFilterQuery = findTargetFilterQueryOrThrowExceptionIfNotFound(
update.getTargetFilterId()); update.getTargetFilterId());
if (update.getDsId() == null) { if (update.getDsId() == null) {
targetFilterQuery.setAccessControlContext(null);
targetFilterQuery.setAutoAssignDistributionSet(null); targetFilterQuery.setAutoAssignDistributionSet(null);
targetFilterQuery.setAutoAssignActionType(null); targetFilterQuery.setAutoAssignActionType(null);
targetFilterQuery.setAutoAssignWeight(null); targetFilterQuery.setAutoAssignWeight(null);
@@ -274,8 +279,10 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
final JpaDistributionSet ds = (JpaDistributionSet) distributionSetManagement final JpaDistributionSet ds = (JpaDistributionSet) distributionSetManagement
.getValidAndComplete(update.getDsId()); .getValidAndComplete(update.getDsId());
verifyDistributionSetAndThrowExceptionIfDeleted(ds); verifyDistributionSetAndThrowExceptionIfDeleted(ds);
targetFilterQuery.setAutoAssignDistributionSet(ds); targetFilterQuery.setAutoAssignDistributionSet(ds);
targetFilterQuery.setAutoAssignInitiatedBy(tenantAware.getCurrentUsername()); contextAware.getCurrentContext().ifPresent(targetFilterQuery::setAccessControlContext);
targetFilterQuery.setAutoAssignInitiatedBy(contextAware.getCurrentUsername());
targetFilterQuery.setAutoAssignActionType(sanitizeAutoAssignActionType(update.getActionType())); targetFilterQuery.setAutoAssignActionType(sanitizeAutoAssignActionType(update.getActionType()));
targetFilterQuery.setAutoAssignWeight(update.getWeight()); targetFilterQuery.setAutoAssignWeight(update.getWeight());
final boolean confirmationRequired = update.isConfirmationRequired() == null ? isConfirmationFlowEnabled() final boolean confirmationRequired = update.isConfirmationRequired() == null ? isConfirmationFlowEnabled()
@@ -325,14 +332,15 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
} }
private void assertMaxTargetsQuota(final String query, final String filterName, final long dsId) { private void assertMaxTargetsQuota(final String query, final String filterName, final long dsId) {
QuotaHelper.assertAssignmentQuota(filterName, targetManagement.countByRsqlAndNonDSAndCompatible(dsId, query), QuotaHelper.assertAssignmentQuota(filterName,
targetManagement.countByRsqlAndNonDSAndCompatibleAndUpdatable(dsId, query),
quotaManagement.getMaxTargetsPerAutoAssignment(), Target.class, TargetFilterQuery.class, null); quotaManagement.getMaxTargetsPerAutoAssignment(), Target.class, TargetFilterQuery.class, null);
} }
@Override @Override
@Transactional @Transactional
public void cancelAutoAssignmentForDistributionSet(final long setId) { public void cancelAutoAssignmentForDistributionSet(final long distributionSetId) {
targetFilterQueryRepository.unsetAutoAssignDistributionSetAndActionType(setId); targetFilterQueryRepository.unsetAutoAssignDistributionSetAndActionTypeAndAccessContext(distributionSetId);
LOGGER.debug("Auto assignments for distribution sets {} deactivated", setId); LOGGER.debug("Auto assignments for distribution sets {} deactivated", distributionSetId);
} }
} }

View File

@@ -7,7 +7,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.management;
import java.net.URI; import java.net.URI;
import java.util.ArrayList; import java.util.ArrayList;
@@ -43,6 +43,8 @@ import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException; import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetCreate; import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetCreate;
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetUpdate; import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetUpdate;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants; import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
@@ -54,6 +56,12 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType; import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_; import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
import org.eclipse.hawkbit.repository.jpa.model.TargetMetadataCompositeKey; import org.eclipse.hawkbit.repository.jpa.model.TargetMetadataCompositeKey;
import org.eclipse.hawkbit.repository.jpa.repository.RolloutGroupRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetFilterQueryRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetMetadataRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetTagRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetTypeRepository;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility; import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder; import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications; import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications;
@@ -83,7 +91,7 @@ import org.springframework.orm.jpa.vendor.Database;
import org.springframework.retry.annotation.Backoff; import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable; import org.springframework.retry.annotation.Retryable;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils; import org.springframework.util.ObjectUtils;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
@@ -118,12 +126,11 @@ public class JpaTargetManagement implements TargetManagement {
private final TenantAware tenantAware; private final TenantAware tenantAware;
private final AfterTransactionCommitExecutor afterCommit;
private final VirtualPropertyReplacer virtualPropertyReplacer; private final VirtualPropertyReplacer virtualPropertyReplacer;
private final Database database; private final Database database;
public JpaTargetManagement(final EntityManager entityManager, public JpaTargetManagement(final EntityManager entityManager,
final DistributionSetManagement distributionSetManagement, final QuotaManagement quotaManagement, final DistributionSetManagement distributionSetManagement, final QuotaManagement quotaManagement,
final TargetRepository targetRepository, final TargetTypeRepository targetTypeRepository, final TargetRepository targetRepository, final TargetTypeRepository targetTypeRepository,
@@ -131,7 +138,7 @@ public class JpaTargetManagement implements TargetManagement {
final RolloutGroupRepository rolloutGroupRepository, final RolloutGroupRepository rolloutGroupRepository,
final TargetFilterQueryRepository targetFilterQueryRepository, final TargetFilterQueryRepository targetFilterQueryRepository,
final TargetTagRepository targetTagRepository, final EventPublisherHolder eventPublisherHolder, final TargetTagRepository targetTagRepository, final EventPublisherHolder eventPublisherHolder,
final TenantAware tenantAware, final AfterTransactionCommitExecutor afterCommit, final TenantAware tenantAware,
final VirtualPropertyReplacer virtualPropertyReplacer, final Database database) { final VirtualPropertyReplacer virtualPropertyReplacer, final Database database) {
this.entityManager = entityManager; this.entityManager = entityManager;
this.distributionSetManagement = distributionSetManagement; this.distributionSetManagement = distributionSetManagement;
@@ -144,7 +151,6 @@ public class JpaTargetManagement implements TargetManagement {
this.targetTagRepository = targetTagRepository; this.targetTagRepository = targetTagRepository;
this.eventPublisherHolder = eventPublisherHolder; this.eventPublisherHolder = eventPublisherHolder;
this.tenantAware = tenantAware; this.tenantAware = tenantAware;
this.afterCommit = afterCommit;
this.virtualPropertyReplacer = virtualPropertyReplacer; this.virtualPropertyReplacer = virtualPropertyReplacer;
this.database = database; this.database = database;
} }
@@ -155,7 +161,8 @@ public class JpaTargetManagement implements TargetManagement {
} }
private JpaTarget getByControllerIdAndThrowIfNotFound(final String controllerId) { private JpaTarget getByControllerIdAndThrowIfNotFound(final String controllerId) {
return targetRepository.findOne(TargetSpecifications.hasControllerId(controllerId)) return targetRepository
.findOne(TargetSpecifications.hasControllerId(controllerId))
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId)); .orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
} }
@@ -179,7 +186,6 @@ public class JpaTargetManagement implements TargetManagement {
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<TargetMetadata> createMetaData(final String controllerId, final Collection<MetaData> md) { public List<TargetMetadata> createMetaData(final String controllerId, final Collection<MetaData> md) {
final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId); final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId);
md.forEach(meta -> checkAndThrowIfTargetMetadataAlreadyExists( md.forEach(meta -> checkAndThrowIfTargetMetadataAlreadyExists(
@@ -222,15 +228,16 @@ public class JpaTargetManagement implements TargetManagement {
// check if exists otherwise throw entity not found exception // check if exists otherwise throw entity not found exception
final JpaTargetMetadata updatedMetadata = (JpaTargetMetadata) getMetaDataByControllerId(controllerId, final JpaTargetMetadata updatedMetadata = (JpaTargetMetadata) getMetaDataByControllerId(controllerId,
md.getKey()).orElseThrow( md.getKey())
() -> new EntityNotFoundException(TargetMetadata.class, controllerId, md.getKey())); .orElseThrow(() -> new EntityNotFoundException(TargetMetadata.class, controllerId, md.getKey()));
updatedMetadata.setValue(md.getValue()); updatedMetadata.setValue(md.getValue());
// touch it to update the lock revision because we are modifying the // touch it to update the lock revision because we are modifying the
// target indirectly // target indirectly
final JpaTarget target = JpaManagementHelper.touch(entityManager, targetRepository, final JpaTarget target = JpaManagementHelper.touch(entityManager, targetRepository,
getByControllerIdAndThrowIfNotFound(controllerId)); getByControllerIdAndThrowIfNotFound(controllerId));
final JpaTargetMetadata metadata = targetMetadataRepository.save(updatedMetadata); final JpaTargetMetadata metadata = targetMetadataRepository.save(updatedMetadata);
// target update event is set to ignore "lastModifiedAt" field so it is // target update event is set to ignore "lastModifiedAt" field, so it is
// not send automatically within the touch() method // not send automatically within the touch() method
eventPublisherHolder.getEventPublisher() eventPublisherHolder.getEventPublisher()
.publishEvent(new TargetUpdatedEvent(target, eventPublisherHolder.getApplicationId())); .publishEvent(new TargetUpdatedEvent(target, eventPublisherHolder.getApplicationId()));
@@ -247,6 +254,7 @@ public class JpaTargetManagement implements TargetManagement {
final JpaTarget target = JpaManagementHelper.touch(entityManager, targetRepository, final JpaTarget target = JpaManagementHelper.touch(entityManager, targetRepository,
getByControllerIdAndThrowIfNotFound(controllerId)); getByControllerIdAndThrowIfNotFound(controllerId));
targetMetadataRepository.deleteById(metadata.getId()); targetMetadataRepository.deleteById(metadata.getId());
// target update event is set to ignore "lastModifiedAt" field, so it is // target update event is set to ignore "lastModifiedAt" field, so it is
// not send automatically within the touch() method // not send automatically within the touch() method
@@ -256,10 +264,10 @@ public class JpaTargetManagement implements TargetManagement {
@Override @Override
public Page<TargetMetadata> findMetaDataByControllerId(final Pageable pageable, final String controllerId) { public Page<TargetMetadata> findMetaDataByControllerId(final Pageable pageable, final String controllerId) {
final Long targetId = getByControllerIdAndThrowIfNotFound(controllerId).getId(); final Long id = getByControllerIdAndThrowIfNotFound(controllerId).getId();
return JpaManagementHelper.findAllWithCountBySpec(targetMetadataRepository, pageable, return JpaManagementHelper.findAllWithCountBySpec(targetMetadataRepository, pageable,
Collections.singletonList(metadataByTargetIdSpec(targetId))); Collections.singletonList(metadataByTargetIdSpec(id)));
} }
private Specification<JpaTargetMetadata> metadataByTargetIdSpec(final Long targetId) { private Specification<JpaTargetMetadata> metadataByTargetIdSpec(final Long targetId) {
@@ -295,7 +303,7 @@ public class JpaTargetManagement implements TargetManagement {
@Override @Override
public Slice<Target> findAll(final Pageable pageable) { public Slice<Target> findAll(final Pageable pageable) {
return JpaManagementHelper.findAllWithoutCountBySpec(targetRepository, pageable, null); return targetRepository.findAllWithoutCount(pageable).map(Target.class::cast);
} }
@Override @Override
@@ -304,14 +312,16 @@ public class JpaTargetManagement implements TargetManagement {
.orElseThrow(() -> new EntityNotFoundException(TargetFilterQuery.class, targetFilterQueryId)); .orElseThrow(() -> new EntityNotFoundException(TargetFilterQuery.class, targetFilterQueryId));
return JpaManagementHelper.findAllWithoutCountBySpec(targetRepository, pageable, return JpaManagementHelper.findAllWithoutCountBySpec(targetRepository, pageable,
Collections.singletonList(RSQLUtility.buildRsqlSpecification(targetFilterQuery.getQuery(), List.of(
TargetFields.class, virtualPropertyReplacer, database))); RSQLUtility.buildRsqlSpecification(targetFilterQuery.getQuery(), TargetFields.class,
virtualPropertyReplacer, database)));
} }
@Override @Override
public Slice<Target> findByRsql(final Pageable pageable, final String targetFilterQuery) { public Slice<Target> findByRsql(final Pageable pageable, final String targetFilterQuery) {
return JpaManagementHelper.findAllWithoutCountBySpec(targetRepository, pageable, return JpaManagementHelper.findAllWithoutCountBySpec(targetRepository, pageable,
Collections.singletonList(RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class, List.of(
RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class,
virtualPropertyReplacer, database))); virtualPropertyReplacer, database)));
} }
@@ -340,48 +350,38 @@ public class JpaTargetManagement implements TargetManagement {
@Transactional @Transactional
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void delete(final Collection<Long> targetIDs) { public void delete(final Collection<Long> ids) {
final List<JpaTarget> targets = targetRepository.findAll(TargetSpecifications.hasIdIn(targetIDs)); final List<JpaTarget> targets = targetRepository.findAllById(ids);
if (targets.size() < ids.size()) {
if (targets.size() < targetIDs.size()) { throw new EntityNotFoundException(Target.class, ids,
throw new EntityNotFoundException(Target.class, targetIDs, targets.stream().map(Target::getId).filter(id -> !ids.contains(id)).toList());
targets.stream().map(Target::getId).collect(Collectors.toList()));
} }
targetRepository.deleteByIdIn(targetIDs); targetRepository.deleteAll(targets);
afterCommit
.afterCommit(() -> targets.forEach(target -> eventPublisherHolder.getEventPublisher()
.publishEvent(new TargetDeletedEvent(tenantAware.getCurrentTenant(), target.getId(),
target.getControllerId(),
Optional.ofNullable(target.getAddress()).map(URI::toString).orElse(null),
JpaTarget.class, eventPublisherHolder.getApplicationId()))));
} }
@Override @Override
@Transactional @Transactional
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteByControllerID(final String controllerID) { public void deleteByControllerID(final String controllerId) {
final Target target = getByControllerIdAndThrowIfNotFound(controllerID); targetRepository.delete(getByControllerIdAndThrowIfNotFound(controllerId));
targetRepository.deleteById(target.getId());
} }
@Override @Override
public Page<Target> findByAssignedDistributionSet(final Pageable pageReq, final long distributionSetID) { public Page<Target> findByAssignedDistributionSet(final Pageable pageReq, final long distributionSetId) {
final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException(distributionSetID); final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException(distributionSetId);
return JpaManagementHelper.findAllWithCountBySpec(targetRepository, pageReq, return JpaManagementHelper.findAllWithCountBySpec(targetRepository, pageReq,
Collections.singletonList(TargetSpecifications.hasAssignedDistributionSet(validDistSet.getId()))); List.of(TargetSpecifications.hasAssignedDistributionSet(validDistSet.getId())));
} }
@Override @Override
public Page<Target> findByAssignedDistributionSetAndRsql(final Pageable pageReq, final long distributionSetID, public Page<Target> findByAssignedDistributionSetAndRsql(final Pageable pageReq, final long distributionSetId,
final String rsqlParam) { final String rsqlParam) {
final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException(distributionSetID); final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException(distributionSetId);
final List<Specification<JpaTarget>> specList = Arrays.asList( final List<Specification<JpaTarget>> specList = List.of(
RSQLUtility.buildRsqlSpecification(rsqlParam, TargetFields.class, virtualPropertyReplacer, database), RSQLUtility.buildRsqlSpecification(rsqlParam, TargetFields.class, virtualPropertyReplacer, database),
TargetSpecifications.hasAssignedDistributionSet(validDistSet.getId())); TargetSpecifications.hasAssignedDistributionSet(validDistSet.getId()));
@@ -389,11 +389,11 @@ public class JpaTargetManagement implements TargetManagement {
} }
@Override @Override
public Page<Target> findByInstalledDistributionSet(final Pageable pageReq, final long distributionSetID) { public Page<Target> findByInstalledDistributionSet(final Pageable pageReq, final long distributionSetId) {
final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException(distributionSetID); final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException(distributionSetId);
return JpaManagementHelper.findAllWithCountBySpec(targetRepository, pageReq, return JpaManagementHelper.findAllWithCountBySpec(targetRepository, pageReq,
Collections.singletonList(TargetSpecifications.hasInstalledDistributionSet(validDistSet.getId()))); List.of(TargetSpecifications.hasInstalledDistributionSet(validDistSet.getId())));
} }
@Override @Override
@@ -401,7 +401,7 @@ public class JpaTargetManagement implements TargetManagement {
final String rsqlParam) { final String rsqlParam) {
final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException(distributionSetId); final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException(distributionSetId);
final List<Specification<JpaTarget>> specList = Arrays.asList( final List<Specification<JpaTarget>> specList = List.of(
RSQLUtility.buildRsqlSpecification(rsqlParam, TargetFields.class, virtualPropertyReplacer, database), RSQLUtility.buildRsqlSpecification(rsqlParam, TargetFields.class, virtualPropertyReplacer, database),
TargetSpecifications.hasInstalledDistributionSet(validDistSet.getId())); TargetSpecifications.hasInstalledDistributionSet(validDistSet.getId()));
@@ -411,7 +411,7 @@ public class JpaTargetManagement implements TargetManagement {
@Override @Override
public Page<Target> findByUpdateStatus(final Pageable pageable, final TargetUpdateStatus status) { public Page<Target> findByUpdateStatus(final Pageable pageable, final TargetUpdateStatus status) {
return JpaManagementHelper.findAllWithCountBySpec(targetRepository, pageable, return JpaManagementHelper.findAllWithCountBySpec(targetRepository, pageable,
Collections.singletonList(TargetSpecifications.hasTargetUpdateStatus(status))); List.of(TargetSpecifications.hasTargetUpdateStatus(status)));
} }
@Override @Override
@@ -440,7 +440,7 @@ public class JpaTargetManagement implements TargetManagement {
specList.add(TargetSpecifications.hasInstalledOrAssignedDistributionSet(validDistSet.getId())); specList.add(TargetSpecifications.hasInstalledOrAssignedDistributionSet(validDistSet.getId()));
} }
if (!StringUtils.isEmpty(filterParams.getFilterBySearchText())) { if (!ObjectUtils.isEmpty(filterParams.getFilterBySearchText())) {
specList.add(TargetSpecifications.likeControllerIdOrName(filterParams.getFilterBySearchText())); specList.add(TargetSpecifications.likeControllerIdOrName(filterParams.getFilterBySearchText()));
} }
if (hasTagsFilterActive(filterParams)) { if (hasTagsFilterActive(filterParams)) {
@@ -481,11 +481,10 @@ public class JpaTargetManagement implements TargetManagement {
final TargetTag tag = targetTagRepository.findByNameEquals(tagName) final TargetTag tag = targetTagRepository.findByNameEquals(tagName)
.orElseThrow(() -> new EntityNotFoundException(TargetTag.class, tagName)); .orElseThrow(() -> new EntityNotFoundException(TargetTag.class, tagName));
final List<JpaTarget> allTargets = targetRepository final List<JpaTarget> allTargets = targetRepository
.findAll(TargetSpecifications.byControllerIdWithTagsInJoin(controllerIds)); .findAll(AccessController.Operation.UPDATE, TargetSpecifications.byControllerIdWithTagsInJoin(controllerIds));
if (allTargets.size() < controllerIds.size()) { if (allTargets.size() < controllerIds.size()) {
throw new EntityNotFoundException(Target.class, controllerIds, throw new EntityNotFoundException(Target.class, controllerIds,
allTargets.stream().map(Target::getControllerId).collect(Collectors.toList())); allTargets.stream().map(Target::getControllerId).toList());
} }
final List<JpaTarget> alreadyAssignedTargets = targetRepository.findAll( final List<JpaTarget> alreadyAssignedTargets = targetRepository.findAll(
@@ -516,7 +515,7 @@ public class JpaTargetManagement implements TargetManagement {
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public TargetTypeAssignmentResult assignType(final Collection<String> controllerIds, final Long typeId) { public TargetTypeAssignmentResult assignType(final Collection<String> controllerIds, final Long typeId) {
final TargetType type = targetTypeRepository.findById(typeId) final JpaTargetType type = targetTypeRepository.findById(typeId)
.orElseThrow(() -> new EntityNotFoundException(TargetType.class, typeId)); .orElseThrow(() -> new EntityNotFoundException(TargetType.class, typeId));
final List<JpaTarget> targetsWithSameType = findTargetsByInSpecification(controllerIds, final List<JpaTarget> targetsWithSameType = findTargetsByInSpecification(controllerIds,
@@ -529,9 +528,7 @@ public class JpaTargetManagement implements TargetManagement {
targetsWithoutSameType.forEach(target -> target.setTargetType(type)); targetsWithoutSameType.forEach(target -> target.setTargetType(type));
final TargetTypeAssignmentResult result = new TargetTypeAssignmentResult(targetsWithSameType.size(), final TargetTypeAssignmentResult result = new TargetTypeAssignmentResult(targetsWithSameType.size(),
Collections.unmodifiableList( targetsWithoutSameType.stream().map(targetRepository::save).toList(), Collections.emptyList(), type);
targetsWithoutSameType.stream().map(targetRepository::save).collect(Collectors.toList())),
Collections.emptyList(), type);
// no reason to persist the type // no reason to persist the type
entityManager.detach(type); entityManager.detach(type);
@@ -542,26 +539,25 @@ public class JpaTargetManagement implements TargetManagement {
@Transactional @Transactional
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public TargetTypeAssignmentResult unAssignType(final Collection<String> controllerIds) { public TargetTypeAssignmentResult unassignType(final Collection<String> controllerIds) {
final List<JpaTarget> allTargets = findTargetsByInSpecification(controllerIds, null); final List<JpaTarget> allTargets = findTargetsByInSpecification(controllerIds, null);
if (allTargets.size() < controllerIds.size()) { if (allTargets.size() < controllerIds.size()) {
throw new EntityNotFoundException(Target.class, controllerIds, throw new EntityNotFoundException(Target.class, controllerIds,
allTargets.stream().map(Target::getControllerId).collect(Collectors.toList())); allTargets.stream().map(Target::getControllerId).toList());
} }
// set new target type to null for all targets // set new target type to null for all targets
allTargets.forEach(target -> target.setTargetType(null)); allTargets.forEach(target -> target.setTargetType(null));
return new TargetTypeAssignmentResult(0, Collections.emptyList(), Collections return new TargetTypeAssignmentResult(0, Collections.emptyList(), targetRepository.saveAll(allTargets), null);
.unmodifiableList(allTargets.stream().map(targetRepository::save).collect(Collectors.toList())), null);
} }
private List<JpaTarget> findTargetsByInSpecification(final Collection<String> controllerIds, private List<JpaTarget> findTargetsByInSpecification(final Collection<String> controllerIds,
final Specification<JpaTarget> specification) { final Specification<JpaTarget> specification) {
return Lists.partition(new ArrayList<>(controllerIds), Constants.MAX_ENTRIES_IN_STATEMENT).stream() return Lists.partition(new ArrayList<>(controllerIds), Constants.MAX_ENTRIES_IN_STATEMENT).stream()
.map(ids -> targetRepository.findAll(TargetSpecifications.hasControllerIdIn(ids).and(specification))) .map(ids -> targetRepository.findAll(TargetSpecifications.hasControllerIdIn(ids).and(specification)))
.flatMap(List::stream).collect(Collectors.toList()); .flatMap(List::stream).toList();
} }
@Override @Override
@@ -569,13 +565,11 @@ public class JpaTargetManagement implements TargetManagement {
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<Target> assignTag(final Collection<String> controllerIds, final long tagId) { public List<Target> assignTag(final Collection<String> controllerIds, final long tagId) {
final List<JpaTarget> allTargets = targetRepository final List<JpaTarget> allTargets = targetRepository
.findAll(TargetSpecifications.byControllerIdWithTagsInJoin(controllerIds)); .findAll(AccessController.Operation.UPDATE, TargetSpecifications.byControllerIdWithTagsInJoin(controllerIds));
if (allTargets.size() < controllerIds.size()) { if (allTargets.size() < controllerIds.size()) {
throw new EntityNotFoundException(Target.class, controllerIds, throw new EntityNotFoundException(Target.class, controllerIds,
allTargets.stream().map(Target::getControllerId).collect(Collectors.toList())); allTargets.stream().map(Target::getControllerId).toList());
} }
final JpaTargetTag tag = targetTagRepository.findById(tagId) final JpaTargetTag tag = targetTagRepository.findById(tagId)
@@ -583,8 +577,7 @@ public class JpaTargetManagement implements TargetManagement {
allTargets.forEach(target -> target.addTag(tag)); allTargets.forEach(target -> target.addTag(tag));
final List<Target> result = allTargets.stream().map(targetRepository::save) final List<Target> result = allTargets.stream().map(targetRepository::save).map(Target.class::cast).toList();
.collect(Collectors.toUnmodifiableList());
// No reason to save the tag // No reason to save the tag
entityManager.detach(tag); entityManager.detach(tag);
@@ -595,8 +588,8 @@ public class JpaTargetManagement implements TargetManagement {
@Transactional @Transactional
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Target unAssignTag(final String controllerID, final long targetTagId) { public Target unassignTag(final String controllerId, final long targetTagId) {
final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerID); final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId);
final TargetTag tag = targetTagRepository.findById(targetTagId) final TargetTag tag = targetTagRepository.findById(targetTagId)
.orElseThrow(() -> new EntityNotFoundException(TargetTag.class, targetTagId)); .orElseThrow(() -> new EntityNotFoundException(TargetTag.class, targetTagId));
@@ -614,8 +607,8 @@ public class JpaTargetManagement implements TargetManagement {
@Transactional @Transactional
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Target unAssignType(final String controllerID) { public Target unassignType(final String controllerId) {
final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerID); final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId);
target.setTargetType(null); target.setTargetType(null);
return targetRepository.save(target); return targetRepository.save(target);
} }
@@ -624,8 +617,8 @@ public class JpaTargetManagement implements TargetManagement {
@Transactional @Transactional
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Target assignType(final String controllerID, final Long targetTypeId) { public Target assignType(final String controllerId, final Long targetTypeId) {
final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerID); final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId);
final JpaTargetType targetType = getTargetTypeByIdAndThrowIfNotFound(targetTypeId); final JpaTargetType targetType = getTargetTypeByIdAndThrowIfNotFound(targetTypeId);
target.setTargetType(targetType); target.setTargetType(targetType);
return targetRepository.save(target); return targetRepository.save(target);
@@ -633,64 +626,67 @@ public class JpaTargetManagement implements TargetManagement {
@Override @Override
public Slice<Target> findByFilterOrderByLinkedDistributionSet(final Pageable pageable, public Slice<Target> findByFilterOrderByLinkedDistributionSet(final Pageable pageable,
final long orderByDistributionId, final FilterParams filterParams) { final long orderByDistributionSetId, final FilterParams filterParams) {
// remove default sort from pageable to not overwrite sorted spec // remove default sort from pageable to not overwrite sorted spec
final OffsetBasedPageRequest unsortedPage = new OffsetBasedPageRequest(pageable.getOffset(), final OffsetBasedPageRequest unsortedPage = new OffsetBasedPageRequest(pageable.getOffset(),
pageable.getPageSize(), Sort.unsorted()); pageable.getPageSize(), Sort.unsorted());
final List<Specification<JpaTarget>> specList = buildSpecificationList(filterParams); final List<Specification<JpaTarget>> specList = buildSpecificationList(filterParams);
specList.add(TargetSpecifications.orderedByLinkedDistributionSet(orderByDistributionId, pageable.getSort())); specList.add(TargetSpecifications.orderedByLinkedDistributionSet(orderByDistributionSetId, pageable.getSort()));
return JpaManagementHelper.findAllWithoutCountBySpec(targetRepository, unsortedPage, specList); return JpaManagementHelper.findAllWithoutCountBySpec(targetRepository, unsortedPage, specList);
} }
@Override @Override
public long countByAssignedDistributionSet(final long distId) { public long countByAssignedDistributionSet(final long distributionSetId) {
final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException((distId)); final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException((distributionSetId));
return targetRepository.count(TargetSpecifications.hasAssignedDistributionSet(validDistSet.getId())); return targetRepository.count(TargetSpecifications.hasAssignedDistributionSet(validDistSet.getId()));
} }
@Override @Override
public long countByInstalledDistributionSet(final long distId) { public long countByInstalledDistributionSet(final long distributionSetId) {
final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException((distId)); final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException((distributionSetId));
return targetRepository.count(TargetSpecifications.hasInstalledDistributionSet(validDistSet.getId())); return targetRepository.count(TargetSpecifications.hasInstalledDistributionSet(validDistSet.getId()));
} }
@Override @Override
public boolean existsByInstalledOrAssignedDistributionSet(final long distId) { public boolean existsByInstalledOrAssignedDistributionSet(final long distributionSetId) {
final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException((distId)); final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException((distributionSetId));
return targetRepository return targetRepository
.exists(TargetSpecifications.hasInstalledOrAssignedDistributionSet(validDistSet.getId())); .exists(TargetSpecifications.hasInstalledOrAssignedDistributionSet(validDistSet.getId()));
} }
@Override @Override
public Slice<Target> findByTargetFilterQueryAndNonDSAndCompatible(final Pageable pageRequest, public Slice<Target> findByTargetFilterQueryAndNonDSAndCompatibleAndUpdatable(final Pageable pageRequest,
final long distributionSetId, final String targetFilterQuery) { final long distributionSetId, final String targetFilterQuery) {
final DistributionSet jpaDistributionSet = distributionSetManagement.getOrElseThrowException(distributionSetId); final DistributionSet jpaDistributionSet = distributionSetManagement.getOrElseThrowException(distributionSetId);
final Long distSetTypeId = jpaDistributionSet.getType().getId(); final Long distSetTypeId = jpaDistributionSet.getType().getId();
final List<Specification<JpaTarget>> specList = Arrays.asList( return targetRepository.findAllWithoutCount(
AccessController.Operation.UPDATE,
JpaManagementHelper.combineWithAnd(List.of(
RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class, virtualPropertyReplacer, RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class, virtualPropertyReplacer,
database), database),
TargetSpecifications.hasNotDistributionSetInActions(distributionSetId), TargetSpecifications.hasNotDistributionSetInActions(distributionSetId),
TargetSpecifications.isCompatibleWithDistributionSetType(distSetTypeId)); TargetSpecifications.isCompatibleWithDistributionSetType(distSetTypeId))),
pageRequest).map(Target.class::cast);
return JpaManagementHelper.findAllWithoutCountBySpec(targetRepository, pageRequest, specList);
} }
@Override @Override
public Slice<Target> findByTargetFilterQueryAndNotInRolloutGroupsAndCompatible(final Pageable pageRequest, public Slice<Target> findByTargetFilterQueryAndNotInRolloutGroupsAndCompatibleAndUpdatable(
final Collection<Long> groups, final String targetFilterQuery, final DistributionSetType dsType) { final Pageable pageRequest, final Collection<Long> groups, final String targetFilterQuery,
final List<Specification<JpaTarget>> specList = Arrays.asList( final DistributionSetType dsType) {
return targetRepository.findAllWithoutCount(
AccessController.Operation.UPDATE,
JpaManagementHelper.combineWithAnd(List.of(
RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class, virtualPropertyReplacer, RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class, virtualPropertyReplacer,
database), database),
TargetSpecifications.isNotInRolloutGroups(groups), TargetSpecifications.isNotInRolloutGroups(groups),
TargetSpecifications.isCompatibleWithDistributionSetType(dsType.getId())); TargetSpecifications.isCompatibleWithDistributionSetType(dsType.getId()))),
pageRequest).map(Target.class::cast);
return JpaManagementHelper.findAllWithoutCountBySpec(targetRepository, pageRequest, specList);
} }
@Override @Override
@@ -711,19 +707,18 @@ public class JpaTargetManagement implements TargetManagement {
} }
return JpaManagementHelper.findAllWithoutCountBySpec(targetRepository, pageRequest, return JpaManagementHelper.findAllWithoutCountBySpec(targetRepository, pageRequest,
Collections.singletonList(TargetSpecifications.hasNoActionInRolloutGroup(group))); List.of(TargetSpecifications.hasNoActionInRolloutGroup(group)));
} }
@Override @Override
public long countByRsqlAndNotInRolloutGroupsAndCompatible(final Collection<Long> groups, public long countByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable(final Collection<Long> groups,
final String targetFilterQuery, final DistributionSetType dsType) { final String targetFilterQuery, final DistributionSetType dsType) {
final List<Specification<JpaTarget>> specList = Arrays.asList( return targetRepository.count(AccessController.Operation.UPDATE, JpaManagementHelper.combineWithAnd(
List.of(
RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class, virtualPropertyReplacer, RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class, virtualPropertyReplacer,
database), database),
TargetSpecifications.isNotInRolloutGroups(groups), TargetSpecifications.isNotInRolloutGroups(groups),
TargetSpecifications.isCompatibleWithDistributionSetType(dsType.getId())); TargetSpecifications.isCompatibleWithDistributionSetType(dsType.getId()))));
return JpaManagementHelper.countBySpec(targetRepository, specList);
} }
@Override @Override
@@ -736,17 +731,17 @@ public class JpaTargetManagement implements TargetManagement {
} }
@Override @Override
public long countByRsqlAndNonDSAndCompatible(final long distributionSetId, final String targetFilterQuery) { public long countByRsqlAndNonDSAndCompatibleAndUpdatable(final long distributionSetId,
final String targetFilterQuery) {
final DistributionSet jpaDistributionSet = distributionSetManagement.getOrElseThrowException(distributionSetId); final DistributionSet jpaDistributionSet = distributionSetManagement.getOrElseThrowException(distributionSetId);
final Long distSetTypeId = jpaDistributionSet.getType().getId(); final Long distSetTypeId = jpaDistributionSet.getType().getId();
final List<Specification<JpaTarget>> specList = Arrays.asList( return targetRepository.count(AccessController.Operation.UPDATE, JpaManagementHelper.combineWithAnd(
List.of(
RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class, virtualPropertyReplacer, RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class, virtualPropertyReplacer,
database), database),
TargetSpecifications.hasNotDistributionSetInActions(distributionSetId), TargetSpecifications.hasNotDistributionSetInActions(distributionSetId),
TargetSpecifications.isCompatibleWithDistributionSetType(distSetTypeId)); TargetSpecifications.isCompatibleWithDistributionSetType(distSetTypeId))));
return JpaManagementHelper.countBySpec(targetRepository, specList);
} }
@Override @Override
@@ -755,7 +750,7 @@ public class JpaTargetManagement implements TargetManagement {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Target create(final TargetCreate c) { public Target create(final TargetCreate c) {
final JpaTargetCreate create = (JpaTargetCreate) c; final JpaTargetCreate create = (JpaTargetCreate) c;
return targetRepository.save(create.build()); return targetRepository.save(AccessController.Operation.CREATE, create.build());
} }
@Override @Override
@@ -764,8 +759,8 @@ public class JpaTargetManagement implements TargetManagement {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<Target> create(final Collection<TargetCreate> targets) { public List<Target> create(final Collection<TargetCreate> targets) {
final List<JpaTarget> targetList = targets.stream().map(JpaTargetCreate.class::cast).map(JpaTargetCreate::build) final List<JpaTarget> targetList = targets.stream().map(JpaTargetCreate.class::cast).map(JpaTargetCreate::build)
.collect(Collectors.toList()); .toList();
return Collections.unmodifiableList(targetRepository.saveAll(targetList)); return Collections.unmodifiableList(targetRepository.saveAll(AccessController.Operation.CREATE, targetList));
} }
@Override @Override
@@ -773,7 +768,7 @@ public class JpaTargetManagement implements TargetManagement {
throwEntityNotFoundExceptionIfTagDoesNotExist(tagId); throwEntityNotFoundExceptionIfTagDoesNotExist(tagId);
return JpaManagementHelper.findAllWithCountBySpec(targetRepository, pageable, return JpaManagementHelper.findAllWithCountBySpec(targetRepository, pageable,
Collections.singletonList(TargetSpecifications.hasTag(tagId))); List.of(TargetSpecifications.hasTag(tagId)));
} }
private void throwEntityNotFoundExceptionIfTagDoesNotExist(final Long tagId) { private void throwEntityNotFoundExceptionIfTagDoesNotExist(final Long tagId) {
@@ -803,15 +798,19 @@ public class JpaTargetManagement implements TargetManagement {
@Override @Override
public long countByRsql(final String targetFilterQuery) { public long countByRsql(final String targetFilterQuery) {
return JpaManagementHelper.countBySpec(targetRepository, Collections.singletonList(RSQLUtility return JpaManagementHelper.countBySpec(
.buildRsqlSpecification(targetFilterQuery, TargetFields.class, virtualPropertyReplacer, database))); targetRepository,
List.of(
RSQLUtility.buildRsqlSpecification(
targetFilterQuery, TargetFields.class, virtualPropertyReplacer, database)));
} }
@Override @Override
public long countByRsqlAndCompatible(final String targetFilterQuery, final Long dsTypeId) { public long countByRsqlAndCompatible(final String targetFilterQuery, final Long distributionSetId) {
final List<Specification<JpaTarget>> specList = Arrays.asList(RSQLUtility final List<Specification<JpaTarget>> specList = List.of(
.buildRsqlSpecification(targetFilterQuery, TargetFields.class, virtualPropertyReplacer, database), RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class, virtualPropertyReplacer,
TargetSpecifications.isCompatibleWithDistributionSetType(dsTypeId)); database),
TargetSpecifications.isCompatibleWithDistributionSetType(distributionSetId));
return JpaManagementHelper.countBySpec(targetRepository, specList); return JpaManagementHelper.countBySpec(targetRepository, specList);
} }
@@ -826,7 +825,7 @@ public class JpaTargetManagement implements TargetManagement {
@Override @Override
public Optional<Target> get(final long id) { public Optional<Target> get(final long id) {
return targetRepository.findById(id).map(t -> t); return targetRepository.findById(id).map(Target.class::cast);
} }
@Override @Override
@@ -836,6 +835,8 @@ public class JpaTargetManagement implements TargetManagement {
@Override @Override
public Map<String, String> getControllerAttributes(final String controllerId) { public Map<String, String> getControllerAttributes(final String controllerId) {
getByControllerIdAndThrowIfNotFound(controllerId);
final CriteriaBuilder cb = entityManager.getCriteriaBuilder(); final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaQuery<Object[]> query = cb.createQuery(Object[].class); final CriteriaQuery<Object[]> query = cb.createQuery(Object[].class);
@@ -855,9 +856,7 @@ public class JpaTargetManagement implements TargetManagement {
@Override @Override
public void requestControllerAttributes(final String controllerId) { public void requestControllerAttributes(final String controllerId) {
final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId); final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId);
target.setRequestControllerAttributes(true); target.setRequestControllerAttributes(true);
eventPublisherHolder.getEventPublisher() eventPublisherHolder.getEventPublisher()
.publishEvent(new TargetAttributesRequestedEvent(tenantAware.getCurrentTenant(), target.getId(), .publishEvent(new TargetAttributesRequestedEvent(tenantAware.getCurrentTenant(), target.getId(),
target.getControllerId(), target.getAddress() != null ? target.getAddress().toString() : null, target.getControllerId(), target.getAddress() != null ? target.getAddress().toString() : null,
@@ -898,7 +897,6 @@ public class JpaTargetManagement implements TargetManagement {
@Override @Override
public Page<Target> findByControllerAttributesRequested(final Pageable pageReq) { public Page<Target> findByControllerAttributesRequested(final Pageable pageReq) {
return JpaManagementHelper.findAllWithCountBySpec(targetRepository, pageReq, return JpaManagementHelper.findAllWithCountBySpec(targetRepository, pageReq,
Collections.singletonList(TargetSpecifications.hasRequestControllerAttributesTrue())); List.of(TargetSpecifications.hasRequestControllerAttributesTrue()));
} }
} }

View File

@@ -7,24 +7,27 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.management;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.repository.TagFields;
import org.eclipse.hawkbit.repository.TargetTagFields; import org.eclipse.hawkbit.repository.TargetTagFields;
import org.eclipse.hawkbit.repository.TargetTagManagement; import org.eclipse.hawkbit.repository.TargetTagManagement;
import org.eclipse.hawkbit.repository.builder.GenericTagUpdate; import org.eclipse.hawkbit.repository.builder.GenericTagUpdate;
import org.eclipse.hawkbit.repository.builder.TagCreate; import org.eclipse.hawkbit.repository.builder.TagCreate;
import org.eclipse.hawkbit.repository.builder.TagUpdate; import org.eclipse.hawkbit.repository.builder.TagUpdate;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.eclipse.hawkbit.repository.jpa.builder.JpaTagCreate; import org.eclipse.hawkbit.repository.jpa.builder.JpaTagCreate;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants; import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag; import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag_;
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetTagRepository;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility; import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.jpa.specifications.TagSpecification; import org.eclipse.hawkbit.repository.jpa.specifications.TagSpecification;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications; import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications;
@@ -49,14 +52,14 @@ import org.springframework.validation.annotation.Validated;
public class JpaTargetTagManagement implements TargetTagManagement { public class JpaTargetTagManagement implements TargetTagManagement {
private final TargetTagRepository targetTagRepository; private final TargetTagRepository targetTagRepository;
private final TargetRepository targetRepository; private final TargetRepository targetRepository;
private final VirtualPropertyReplacer virtualPropertyReplacer; private final VirtualPropertyReplacer virtualPropertyReplacer;
private final Database database; private final Database database;
public JpaTargetTagManagement(final TargetTagRepository targetTagRepository, public JpaTargetTagManagement(
final TargetRepository targetRepository, final VirtualPropertyReplacer virtualPropertyReplacer, final TargetTagRepository targetTagRepository, final TargetRepository targetRepository,
final VirtualPropertyReplacer virtualPropertyReplacer,
final Database database) { final Database database) {
this.targetTagRepository = targetTagRepository; this.targetTagRepository = targetTagRepository;
this.targetRepository = targetRepository; this.targetRepository = targetRepository;
@@ -76,7 +79,7 @@ public class JpaTargetTagManagement implements TargetTagManagement {
public TargetTag create(final TagCreate c) { public TargetTag create(final TagCreate c) {
final JpaTagCreate create = (JpaTagCreate) c; final JpaTagCreate create = (JpaTagCreate) c;
return targetTagRepository.save(create.buildTargetTag()); return targetTagRepository.save(AccessController.Operation.CREATE, create.buildTargetTag());
} }
@Override @Override
@@ -84,11 +87,10 @@ public class JpaTargetTagManagement implements TargetTagManagement {
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<TargetTag> create(final Collection<TagCreate> tt) { public List<TargetTag> create(final Collection<TagCreate> tt) {
@SuppressWarnings({ "unchecked", "rawtypes" }) final List<JpaTargetTag> targetTagList = tt.stream().map(JpaTagCreate.class::cast)
final Collection<JpaTagCreate> targetTags = (Collection) tt; .map(JpaTagCreate::buildTargetTag).toList();
return Collections.unmodifiableList(
return Collections.unmodifiableList(targetTags.stream() targetTagRepository.saveAll(AccessController.Operation.CREATE, targetTagList));
.map(ttc -> targetTagRepository.save(ttc.buildTargetTag())).collect(Collectors.toList()));
} }
@Override @Override
@@ -96,12 +98,10 @@ public class JpaTargetTagManagement implements TargetTagManagement {
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void delete(final String targetTagName) { public void delete(final String targetTagName) {
if (!targetTagRepository.existsByName(targetTagName)) { targetTagRepository.delete(
throw new EntityNotFoundException(TargetTag.class, targetTagName); targetTagRepository
} .findOne(((root, query, cb) -> cb.equal(root.get(JpaTargetTag_.name), targetTagName)))
.orElseThrow(() -> new EntityNotFoundException(TargetTag.class, targetTagName)));
// finally delete the tag itself
targetTagRepository.deleteByName(targetTagName);
} }
@Override @Override

View File

@@ -7,13 +7,12 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.management;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.repository.QuotaManagement; import org.eclipse.hawkbit.repository.QuotaManagement;
import org.eclipse.hawkbit.repository.TargetTypeFields; import org.eclipse.hawkbit.repository.TargetTypeFields;
@@ -24,10 +23,15 @@ import org.eclipse.hawkbit.repository.builder.TargetTypeUpdate;
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException; import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.TargetTypeInUseException; import org.eclipse.hawkbit.repository.exception.TargetTypeInUseException;
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetTypeCreate; import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetTypeCreate;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants; import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType; import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetTypeRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetTypeRepository;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility; import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetTypeSpecification; import org.eclipse.hawkbit.repository.jpa.specifications.TargetTypeSpecification;
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper; import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
@@ -87,7 +91,7 @@ public class JpaTargetTypeManagement implements TargetTypeManagement {
@Override @Override
public Optional<TargetType> getByName(final String name) { public Optional<TargetType> getByName(final String name) {
return targetTypeRepository.findByName(name).map(TargetType.class::cast); return targetTypeRepository.findOne(TargetTypeSpecification.hasName(name)).map(TargetType.class::cast);
} }
@Override @Override
@@ -97,7 +101,7 @@ public class JpaTargetTypeManagement implements TargetTypeManagement {
@Override @Override
public long countByName(final String name) { public long countByName(final String name) {
return targetTypeRepository.countByName(name); return targetTypeRepository.count(TargetTypeSpecification.hasName(name));
} }
@Override @Override
@@ -105,8 +109,8 @@ public class JpaTargetTypeManagement implements TargetTypeManagement {
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public TargetType create(final TargetTypeCreate create) { public TargetType create(final TargetTypeCreate create) {
final JpaTargetTypeCreate typeCreate = (JpaTargetTypeCreate) create; final JpaTargetType typeCreate = ((JpaTargetTypeCreate) create).build();
return targetTypeRepository.save(typeCreate.build()); return targetTypeRepository.save(AccessController.Operation.CREATE, typeCreate);
} }
@Override @Override
@@ -114,44 +118,47 @@ public class JpaTargetTypeManagement implements TargetTypeManagement {
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<TargetType> create(final Collection<TargetTypeCreate> creates) { public List<TargetType> create(final Collection<TargetTypeCreate> creates) {
return creates.stream().map(this::create).collect(Collectors.toList()); final List<JpaTargetType> typeCreate =
creates.stream().map(create -> ((JpaTargetTypeCreate) create).build()).toList();
return Collections.unmodifiableList(targetTypeRepository.saveAll(AccessController.Operation.CREATE, typeCreate));
} }
@Override @Override
@Transactional @Transactional
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void delete(final Long targetTypeId) { public void delete(final Long id) {
throwExceptionIfTargetTypeDoesNotExist(targetTypeId); getByIdAndThrowIfNotFound(id);
if (targetRepository.countByTargetTypeId(targetTypeId) > 0) { if (targetRepository.countByTargetTypeId(id) > 0) {
throw new TargetTypeInUseException("Cannot delete target type that is in use"); throw new TargetTypeInUseException("Cannot delete target type that is in use");
} }
targetTypeRepository.deleteById(targetTypeId); targetTypeRepository.deleteById(id);
} }
@Override @Override
public Slice<TargetType> findAll(final Pageable pageable) { public Slice<TargetType> findAll(final Pageable pageable) {
return JpaManagementHelper.findAllWithoutCountBySpec(targetTypeRepository, pageable, null); return targetTypeRepository.findAllWithoutCount(pageable).map(TargetType.class::cast);
} }
@Override @Override
public Page<TargetType> findByRsql(final Pageable pageable, final String rsqlParam) { public Page<TargetType> findByRsql(final Pageable pageable, final String rsqlParam) {
return JpaManagementHelper.findAllWithCountBySpec(targetTypeRepository, pageable, return JpaManagementHelper.findAllWithCountBySpec(targetTypeRepository, pageable,
Collections.singletonList(RSQLUtility.buildRsqlSpecification(rsqlParam, TargetTypeFields.class, List.of(
virtualPropertyReplacer, database))); RSQLUtility.buildRsqlSpecification(
rsqlParam, TargetTypeFields.class, virtualPropertyReplacer,database)));
} }
@Override @Override
public Slice<TargetType> findByName(final Pageable pageable, final String name) { public Slice<TargetType> findByName(final Pageable pageable, final String name) {
return JpaManagementHelper.findAllWithoutCountBySpec(targetTypeRepository, pageable, return JpaManagementHelper.findAllWithoutCountBySpec(targetTypeRepository, pageable,
Collections.singletonList(TargetTypeSpecification.likeName(name))); List.of(TargetTypeSpecification.likeName(name)));
} }
@Override @Override
public Optional<TargetType> get(final long id) { public Optional<TargetType> get(final long id) {
return targetTypeRepository.findById(id).map(targetType -> targetType); return targetTypeRepository.findById(id).map(TargetType.class::cast);
} }
@Override @Override
@@ -161,20 +168,21 @@ public class JpaTargetTypeManagement implements TargetTypeManagement {
@Override @Override
public List<TargetType> findByTargetIds(final Collection<Long> targetIds) { public List<TargetType> findByTargetIds(final Collection<Long> targetIds) {
return targetTypeRepository.findAll(TargetTypeSpecification.hasTarget(targetIds)).stream() return targetTypeRepository
.map(TargetType.class::cast).collect(Collectors.toList()); .findAll(TargetTypeSpecification.hasTarget(targetIds)).stream().map(TargetType.class::cast).toList();
} }
@Override @Override
public Optional<TargetType> findByTargetControllerId(final String controllerId) { public Optional<TargetType> findByTargetControllerId(final String controllerId) {
return targetTypeRepository.findOne(TargetTypeSpecification.hasTargetControllerId(controllerId)) return targetTypeRepository
.map(TargetType.class::cast); .findOne(TargetTypeSpecification.hasTargetControllerId(controllerId)).map(TargetType.class::cast);
} }
@Override @Override
public List<TargetType> findByTargetControllerIds(final Collection<String> controllerIds) { public List<TargetType> findByTargetControllerIds(final Collection<String> controllerIds) {
return targetTypeRepository.findAll(TargetTypeSpecification.hasTargetControllerIdIn(controllerIds)).stream() return targetTypeRepository
.map(TargetType.class::cast).collect(Collectors.toList()); .findAll(TargetTypeSpecification.hasTargetControllerIdIn(controllerIds))
.stream().map(TargetType.class::cast).toList();
} }
@Override @Override
@@ -189,7 +197,7 @@ public class JpaTargetTypeManagement implements TargetTypeManagement {
public TargetType update(final TargetTypeUpdate update) { public TargetType update(final TargetTypeUpdate update) {
final GenericTargetTypeUpdate typeUpdate = (GenericTargetTypeUpdate) update; final GenericTargetTypeUpdate typeUpdate = (GenericTargetTypeUpdate) update;
final JpaTargetType type = findTargetTypeAndThrowExceptionIfNotFound(typeUpdate.getId()); final JpaTargetType type = getByIdAndThrowIfNotFound(typeUpdate.getId());
typeUpdate.getName().ifPresent((type::setName)); typeUpdate.getName().ifPresent((type::setName));
typeUpdate.getDescription().ifPresent(type::setDescription); typeUpdate.getDescription().ifPresent(type::setDescription);
@@ -202,19 +210,18 @@ public class JpaTargetTypeManagement implements TargetTypeManagement {
@Transactional @Transactional
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public TargetType assignCompatibleDistributionSetTypes(final long targetTypeId, public TargetType assignCompatibleDistributionSetTypes(final long id,
final Collection<Long> distributionSetTypeIds) { final Collection<Long> distributionSetTypeIds) {
final Collection<JpaDistributionSetType> dsTypes = distributionSetTypeRepository final Collection<JpaDistributionSetType> dsTypes = distributionSetTypeRepository
.findAllById(distributionSetTypeIds); .findAllById(distributionSetTypeIds);
if (dsTypes.size() < distributionSetTypeIds.size()) { if (dsTypes.size() < distributionSetTypeIds.size()) {
throw new EntityNotFoundException(DistributionSetType.class, distributionSetTypeIds, throw new EntityNotFoundException(DistributionSetType.class, distributionSetTypeIds,
dsTypes.stream().map(DistributionSetType::getId).collect(Collectors.toList())); dsTypes.stream().map(DistributionSetType::getId).toList());
} }
final JpaTargetType type = findTargetTypeAndThrowExceptionIfNotFound(targetTypeId); final JpaTargetType type = getByIdAndThrowIfNotFound(id);
assertDistributionSetTypeQuota(targetTypeId, distributionSetTypeIds.size()); assertDistributionSetTypeQuota(id, distributionSetTypeIds.size());
dsTypes.forEach(type::addCompatibleDistributionSetType); dsTypes.forEach(type::addCompatibleDistributionSetType);
return targetTypeRepository.save(type); return targetTypeRepository.save(type);
@@ -224,27 +231,24 @@ public class JpaTargetTypeManagement implements TargetTypeManagement {
@Transactional @Transactional
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public TargetType unassignDistributionSetType(final long targetTypeId, final long distributionSetTypeId) { public TargetType unassignDistributionSetType(final long id, final long distributionSetTypeId) {
final JpaTargetType type = findTargetTypeAndThrowExceptionIfNotFound(targetTypeId); final JpaTargetType type = getByIdAndThrowIfNotFound(id);
findDsTypeAndThrowExceptionIfNotFound(distributionSetTypeId); assertDistributionSetTypeExists(distributionSetTypeId);
type.removeDistributionSetType(distributionSetTypeId); type.removeDistributionSetType(distributionSetTypeId);
return targetTypeRepository.save(type); return targetTypeRepository.save(type);
} }
private JpaTargetType findTargetTypeAndThrowExceptionIfNotFound(final Long typeId) { private void assertDistributionSetTypeExists(final Long typeId) {
return (JpaTargetType) get(typeId).orElseThrow(() -> new EntityNotFoundException(TargetType.class, typeId)); distributionSetTypeRepository
} .findById(typeId)
private JpaDistributionSetType findDsTypeAndThrowExceptionIfNotFound(final Long typeId) {
return distributionSetTypeRepository.findById(typeId)
.orElseThrow(() -> new EntityNotFoundException(DistributionSetType.class, typeId)); .orElseThrow(() -> new EntityNotFoundException(DistributionSetType.class, typeId));
} }
private void throwExceptionIfTargetTypeDoesNotExist(final Long typeId) { private JpaTargetType getByIdAndThrowIfNotFound(final Long id) {
if (!targetTypeRepository.existsById(typeId)) { return targetTypeRepository
throw new EntityNotFoundException(TargetType.class, typeId); .findById(id).orElseThrow(() -> new EntityNotFoundException(TargetType.class, id));
}
} }
/** /**

View File

@@ -7,7 +7,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.management;
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.BATCH_ASSIGNMENTS_ENABLED; import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.BATCH_ASSIGNMENTS_ENABLED;
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED; import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED;
@@ -25,6 +25,7 @@ import org.eclipse.hawkbit.repository.exception.TenantConfigurationValueChangeNo
import org.eclipse.hawkbit.repository.jpa.configuration.Constants; import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor; import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
import org.eclipse.hawkbit.repository.jpa.model.JpaTenantConfiguration; import org.eclipse.hawkbit.repository.jpa.model.JpaTenantConfiguration;
import org.eclipse.hawkbit.repository.jpa.repository.TenantConfigurationRepository;
import org.eclipse.hawkbit.repository.model.TenantConfiguration; import org.eclipse.hawkbit.repository.model.TenantConfiguration;
import org.eclipse.hawkbit.repository.model.TenantConfigurationValue; import org.eclipse.hawkbit.repository.model.TenantConfigurationValue;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties;

View File

@@ -7,9 +7,12 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.management;
import org.eclipse.hawkbit.repository.TenantStatsManagement; import org.eclipse.hawkbit.repository.TenantStatsManagement;
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
import org.eclipse.hawkbit.repository.jpa.repository.LocalArtifactRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
import org.eclipse.hawkbit.repository.report.model.TenantUsage; import org.eclipse.hawkbit.repository.report.model.TenantUsage;
import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -45,11 +48,9 @@ public class JpaTenantStatsManagement implements TenantStatsManagement {
result.setTargets(targetRepository.count()); result.setTargets(targetRepository.count());
result.setArtifacts(artifactRepository.countBySoftwareModuleDeleted(false)); result.setArtifacts(artifactRepository.countBySoftwareModuleDeleted(false));
artifactRepository.getSumOfUndeletedArtifactSize().ifPresent(result::setOverallArtifactVolumeInBytes); artifactRepository.sumOfNonDeletedArtifactSize().ifPresent(result::setOverallArtifactVolumeInBytes);
result.setActions(actionRepository.count()); result.setActions(actionRepository.count());
return result; return result;
} }
} }

View File

@@ -7,7 +7,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.management;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
@@ -19,12 +19,17 @@ import java.util.stream.Collectors;
import org.eclipse.hawkbit.repository.QuotaManagement; import org.eclipse.hawkbit.repository.QuotaManagement;
import org.eclipse.hawkbit.repository.RepositoryConstants; import org.eclipse.hawkbit.repository.RepositoryConstants;
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants; import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor; import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction; import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus; import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget; import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
import org.eclipse.hawkbit.repository.jpa.repository.ActionStatusRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder; import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications; import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications;
import org.eclipse.hawkbit.repository.model.Action.Status; import org.eclipse.hawkbit.repository.model.Action.Status;
@@ -85,10 +90,30 @@ public class OfflineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
} }
@Override @Override
public void setAssignedDistributionSetAndTargetStatus(final JpaDistributionSet set, final List<List<Long>> targetIds, public void setAssignedDistributionSetAndTargetStatus(
final String currentUser) { final JpaDistributionSet set, final List<List<Long>> targetIds, final String currentUser) {
targetIds.forEach(tIds -> targetRepository.setAssignedAndInstalledDistributionSetAndUpdateStatus( final long now = System.currentTimeMillis();
TargetUpdateStatus.IN_SYNC, set, System.currentTimeMillis(), currentUser, tIds)); targetIds.forEach(targetIdsChunk -> {
if (targetRepository.count(AccessController.Operation.UPDATE, targetRepository.byIdsSpec(targetIdsChunk)) != targetIdsChunk.size()) {
throw new InsufficientPermissionException("No update access to all targets!");
}
targetRepository.setAssignedAndInstalledDistributionSetAndUpdateStatus(
TargetUpdateStatus.IN_SYNC, set, now, currentUser, targetIdsChunk);
// TODO AC - current problem with this approach is that the caller detach the targets and seems doesn't save them
// targetRepository.saveAll(
// targetRepository
// .findAll(AccessController.Operation.UPDATE, targetRepository.byIdsSpec(targetIdsChunk))
// .stream()
// .peek(target -> {
// target.setAssignedDistributionSet(set);
// target.setInstalledDistributionSet(set);
// target.setInstallationDate(now);
// target.setLastModifiedAt(now);
// target.setLastModifiedBy(currentUser);
// target.setUpdateStatus(TargetUpdateStatus.IN_SYNC);
// })
// .toList());
});
} }
@Override @Override

View File

@@ -7,7 +7,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.management;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@@ -22,12 +22,17 @@ import org.eclipse.hawkbit.repository.QuotaManagement;
import org.eclipse.hawkbit.repository.event.remote.MultiActionAssignEvent; import org.eclipse.hawkbit.repository.event.remote.MultiActionAssignEvent;
import org.eclipse.hawkbit.repository.event.remote.MultiActionCancelEvent; import org.eclipse.hawkbit.repository.event.remote.MultiActionCancelEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent; import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants; import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor; import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction; import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus; import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget; import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
import org.eclipse.hawkbit.repository.jpa.repository.ActionStatusRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications; import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.Status; import org.eclipse.hawkbit.repository.model.Action.Status;
@@ -122,9 +127,26 @@ public class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
@Override @Override
public void setAssignedDistributionSetAndTargetStatus(final JpaDistributionSet set, final List<List<Long>> targetIds, public void setAssignedDistributionSetAndTargetStatus(final JpaDistributionSet set, final List<List<Long>> targetIds,
final String currentUser) { final String currentUser) {
targetIds.forEach(tIds -> targetRepository.setAssignedDistributionSetAndUpdateStatus(TargetUpdateStatus.PENDING, final long now = System.currentTimeMillis();
set, System.currentTimeMillis(), currentUser, tIds)); targetIds.forEach(targetIdsChunk -> {
if (targetRepository.count(AccessController.Operation.UPDATE, targetRepository.byIdsSpec(targetIdsChunk)) != targetIdsChunk.size()) {
throw new InsufficientPermissionException("No update access to all targets!");
}
targetRepository.setAssignedDistributionSetAndUpdateStatus(TargetUpdateStatus.PENDING,
set, now, currentUser, targetIdsChunk);
// TODO AC - current problem with this approach is that the caller detach the targets and seems doesn't save them
// targetRepository.saveAll(
// targetRepository
// .findAll(AccessController.Operation.UPDATE, targetRepository.byIdsSpec(targetIdsChunk))
// .stream()
// .peek(target -> {
// target.setAssignedDistributionSet(set);
// target.setLastModifiedAt(now);
// target.setLastModifiedBy(currentUser);
// target.setUpdateStatus(TargetUpdateStatus.PENDING);
// })
// .toList());
});
} }
@Override @Override

View File

@@ -56,9 +56,12 @@ import org.eclipse.persistence.descriptors.DescriptorEvent;
@Table(name = "sp_action", indexes = { @Index(name = "sp_idx_action_01", columnList = "tenant,distribution_set"), @Table(name = "sp_action", indexes = { @Index(name = "sp_idx_action_01", columnList = "tenant,distribution_set"),
@Index(name = "sp_idx_action_02", columnList = "tenant,target,active"), @Index(name = "sp_idx_action_02", columnList = "tenant,target,active"),
@Index(name = "sp_idx_action_prim", columnList = "tenant,id") }) @Index(name = "sp_idx_action_prim", columnList = "tenant,id") })
@NamedEntityGraphs({ @NamedEntityGraph(name = "Action.ds", attributeNodes = { @NamedAttributeNode("distributionSet") }), @NamedEntityGraphs({
@NamedEntityGraph(name = "Action.all", attributeNodes = { @NamedAttributeNode("distributionSet"), @NamedEntityGraph(name = "Action.ds", attributeNodes = { @NamedAttributeNode("distributionSet") }),
@NamedAttributeNode(value = "target", subgraph = "target.ds") }, subgraphs = @NamedSubgraph(name = "target.ds", attributeNodes = @NamedAttributeNode("assignedDistributionSet"))) }) @NamedEntityGraph(name = "Action.all", attributeNodes = {
@NamedAttributeNode("distributionSet"),
@NamedAttributeNode(value = "target", subgraph = "target.ds") },
subgraphs = @NamedSubgraph(name = "target.ds", attributeNodes = @NamedAttributeNode("assignedDistributionSet"))) })
@Entity @Entity
// exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for // exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for
// sub entities // sub entities

View File

@@ -28,7 +28,7 @@ import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
/** /**
* JPA implementation of {@link LocalArtifact}. * JPA implementation of {@link Artifact}.
* *
*/ */
@Table(name = "sp_artifact", indexes = { @Index(name = "sp_idx_artifact_01", columnList = "tenant,software_module"), @Table(name = "sp_artifact", indexes = { @Index(name = "sp_idx_artifact_01", columnList = "tenant,software_module"),

View File

@@ -66,14 +66,6 @@ public class JpaDistributionSetTag extends JpaTag implements DistributionSetTag,
// Default constructor for JPA. // Default constructor for JPA.
} }
public List<DistributionSet> getAssignedToDistributionSet() {
if (assignedToDistributionSet == null) {
return Collections.emptyList();
}
return Collections.unmodifiableList(assignedToDistributionSet);
}
@Override @Override
public void fireCreateEvent(final DescriptorEvent descriptorEvent) { public void fireCreateEvent(final DescriptorEvent descriptorEvent) {
EventPublisherHolder.getInstance().getEventPublisher().publishEvent( EventPublisherHolder.getInstance().getEventPublisher().publishEvent(
@@ -84,7 +76,6 @@ public class JpaDistributionSetTag extends JpaTag implements DistributionSetTag,
public void fireUpdateEvent(final DescriptorEvent descriptorEvent) { public void fireUpdateEvent(final DescriptorEvent descriptorEvent) {
EventPublisherHolder.getInstance().getEventPublisher().publishEvent( EventPublisherHolder.getInstance().getEventPublisher().publishEvent(
new DistributionSetTagUpdatedEvent(this, EventPublisherHolder.getInstance().getApplicationId())); new DistributionSetTagUpdatedEvent(this, EventPublisherHolder.getInstance().getApplicationId()));
} }
@Override @Override

View File

@@ -140,6 +140,9 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
@Max(Action.WEIGHT_MAX) @Max(Action.WEIGHT_MAX)
private Integer weight; private Integer weight;
@Column(name = "access_control_context", nullable = true)
private String accessControlContext;
@Transient @Transient
private transient TotalTargetCountStatus totalTargetCountStatus; private transient TotalTargetCountStatus totalTargetCountStatus;
@@ -222,6 +225,14 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
this.weight = weight; this.weight = weight;
} }
public Optional<String> getAccessControlContext() {
return Optional.ofNullable(accessControlContext);
}
public void setAccessControlContext(final String accessControlContext) {
this.accessControlContext = accessControlContext;
}
@Override @Override
public long getTotalTargets() { public long getTotalTargets() {
return totalTargets; return totalTargets;

View File

@@ -84,6 +84,9 @@ public class JpaTargetFilterQuery extends AbstractJpaTenantAwareBaseEntity
@Column(name = "confirmation_required") @Column(name = "confirmation_required")
private boolean confirmationRequired; private boolean confirmationRequired;
@Column(name = "access_control_context", nullable = true)
private String accessControlContext;
public JpaTargetFilterQuery() { public JpaTargetFilterQuery() {
// Default constructor for JPA. // Default constructor for JPA.
} }
@@ -176,6 +179,14 @@ public class JpaTargetFilterQuery extends AbstractJpaTenantAwareBaseEntity
this.confirmationRequired = confirmationRequired; this.confirmationRequired = confirmationRequired;
} }
public Optional<String> getAccessControlContext() {
return Optional.ofNullable(accessControlContext);
}
public void setAccessControlContext(final String accessControlContext) {
this.accessControlContext = accessControlContext;
}
@Override @Override
public void fireCreateEvent(final DescriptorEvent descriptorEvent) { public void fireCreateEvent(final DescriptorEvent descriptorEvent) {
EventPublisherHolder.getInstance().getEventPublisher().publishEvent( EventPublisherHolder.getInstance().getEventPublisher().publishEvent(

View File

@@ -0,0 +1,111 @@
/**
* Copyright (c) 2023 Bosch.IO GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.repository;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import java.util.List;
import java.util.Optional;
/**
* Repository interface that offers some actions that takes in account a target operation.
*
* @param <T> entity type
*/
public interface ACMRepository<T> {
/**
* Saves only if the caller have access for the operation over the entity. This method could be used to
* check CREATE access in creating an entity (save without operation would check for UPDATE access).
*
* @param operation access operationIf operation is <code>null</code> no access is checked! Should be used
* only for tenant context.
* @param entity the entity to save
* @return the saved entity
*/
@NonNull
<S extends T> S save(@Nullable AccessController.Operation operation, @NonNull final S entity);
/**
* Saves only if the caller have access for the operation over all entities. This method could be used to
* check CREATE access in creating an entity (save without operation would check for UPDATE access).
*
* @param operation access operationIf operation is <code>null</code> no access is checked! Should be used
* only for tenant context.
* @param entities the entities to save
* @return the saved entities
*/
<S extends T> List<S> saveAll(@Nullable AccessController.Operation operation, final Iterable<S> entities);
/**
* Returns single entry that match specification and the operation is allowed for.
*
* @param operation access operation. If operation is <code>null</code> no access is checked! Should be used
* only for tenant context.
* @param spec specification
* @return matching entity
*/
@NonNull
Optional<T> findOne(@Nullable AccessController.Operation operation, @Nullable Specification<T> spec);
/**
* Returns all entries that match specification and the operation is allowed for.
*
* @param operation access operation. If operation is <code>null</code> no access is checked! Should be used
* only for tenant context.
* @param spec specification
* @return matching entities
*/
@NonNull
List<T> findAll(@Nullable AccessController.Operation operation, @Nullable Specification<T> spec);
/**
* Returns all entries that match specification and the operation is allowed for.
*
* @param operation access operation. If operation is <code>null</code> no access is checked! Should be used
* only for tenant context.
* @param spec specification
* @return matching entities
*/
@NonNull
boolean exists(@Nullable AccessController.Operation operation, Specification<T> spec);
/**
* Returns count of all entries that match specification and the operation is allowed for.
*
* @param operation access operation. If operation is <code>null</code> no access is checked! Should be used
* only for tenant context.
* @param spec specification
* @return count of matching entities
*/
@NonNull
long count(@Nullable AccessController.Operation operation, @Nullable Specification<T> spec);
/**
* Returns all entries, without count, that match specification and the operation is allowed for.
*
* @param operation access operation. If operation is <code>null</code> no access is checked! Should be used
* only for tenant context.
* @param spec specification
* @param pageable pageable
* @return count of matching entities
*/
@NonNull
Slice<T> findAllWithoutCount(
@Nullable final AccessController.Operation operation, @Nullable Specification<T> spec, Pageable pageable);
@NonNull
Class<T> getDomainClass();
}

View File

@@ -7,7 +7,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.repository;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@@ -23,15 +23,11 @@ import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Rollout; import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.RolloutGroup; import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus; import org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType; import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
@@ -42,7 +38,8 @@ import org.springframework.transaction.annotation.Transactional;
* *
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true)
public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>, JpaSpecificationExecutor<JpaAction> { public interface ActionRepository extends BaseEntityRepository<JpaAction> {
/** /**
* Retrieves an Action with all lazy attributes. * Retrieves an Action with all lazy attributes.
* *
@@ -51,144 +48,7 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
* @return the found {@link Action} * @return the found {@link Action}
*/ */
@EntityGraph(value = "Action.all", type = EntityGraphType.LOAD) @EntityGraph(value = "Action.all", type = EntityGraphType.LOAD)
Optional<Action> getActionById(Long actionId); Optional<Action> findWithDetailsById(Long actionId);
/**
* Retrieves all {@link Action}s which are referring the given
* {@link DistributionSet}.
*
* @param pageable
* page parameters
* @param dsId
* the {@link DistributionSet} on which will be filtered
* @return the found {@link Action}s
*/
Page<Action> findByDistributionSetId(Pageable pageable, Long dsId);
/**
* Retrieves all active {@link Action}s which are referring the given
* {@link DistributionSet}.
*
* @param set
* the {@link DistributionSet} on which will be filtered
* @return the found {@link Action}s
*/
List<Action> findByDistributionSetAndActiveIsTrue(DistributionSet set);
/**
* Retrieves all active {@link Action}s which are referring the given
* {@link DistributionSet} and are not in the given state
*
* @param set
* the {@link DistributionSet} on which will be filtered
* @param status
* the state the actions should not have
* @return the found {@link Action}s
*/
List<Action> findByDistributionSetAndActiveIsTrueAndStatusIsNot(DistributionSet set, Status status);
/**
* Retrieves all {@link Action}s which are referring the given
* {@link Target}.
*
* @param pageable
* page parameters
* @param controllerId
* the target to find assigned actions
* @return the found {@link Action}s
*/
Slice<Action> findByTargetControllerId(Pageable pageable, String controllerId);
/**
* Retrieves all {@link Action}s which are referring the given targetId
*
* @param pageable
* page parameters
* @param targetId
* the target to find assigned actions for
* @return the found {@link Action}s
*/
Page<Action> findByTargetId(Pageable pageable, Long targetId);
/**
* Retrieves all {@link Action}s which are active and referring to the given
* {@link Target} order by ID ascending.
*
* @param target
* the target to find assigned actions
* @param active
* the action active flag
* @return the found {@link Action}s
*/
List<Action> findByTargetAndActiveOrderByIdAsc(JpaTarget target, boolean active);
/**
* Retrieves the active {@link Action}s with the highest weights that refer
* to the given {@link Target}. If {@link Action}s have the same weight they
* are ordered ascending by ID (oldest ones first).
*
* @param pageable
* pageable
* @param controllerId
* the target to find assigned actions
* @return the found {@link Action}s
*/
@EntityGraph(value = "Action.ds", type = EntityGraphType.LOAD)
Page<Action> findByTargetControllerIdAndActiveIsTrueAndWeightIsNotNullOrderByWeightDescIdAsc(Pageable pageable,
String controllerId);
/**
* Retrieves the active {@link Action}s with the lowest IDs (the oldest one)
* whose weight is null and that that refers to the given {@link Target}.
*
* @param pageable
* pageable
* @param controllerId
* the target to find assigned actions
* @return the found {@link Action}s
*/
@EntityGraph(value = "Action.ds", type = EntityGraphType.LOAD)
Page<Action> findByTargetControllerIdAndActiveIsTrueAndWeightIsNullOrderByIdAsc(Pageable pageable,
String controllerId);
/**
* Checks if an active action exists for given
* {@link Target#getControllerId()}.
*
* @param controllerId
* of target to check
* @return <code>true</code> if an active action for the target exists.
*/
@Query("SELECT CASE WHEN COUNT(a)>0 THEN 'true' ELSE 'false' END FROM JpaAction a JOIN a.target t WHERE t.controllerId=:controllerId AND a.active=1")
boolean activeActionExistsForControllerId(@Param("controllerId") String controllerId);
/**
* Check if any active actions with given action status and given controller
* ID exist.
*
* @param controllerId
* of the target to check for actions
* @param currentStatus
* of the active action to look for
*
* @return <code>true</code> if one or more active actions for the given
* controllerId and action status are found
*/
boolean existsByTargetControllerIdAndStatusAndActiveIsTrue(String controllerId, Action.Status currentStatus);
/**
* Retrieves latest {@link Action} for given target and
* {@link SoftwareModule}.
*
* @param targetId
* to search for
* @param moduleId
* to search for
* @return action if there is one with assigned target and module is part of
* assigned {@link DistributionSet}.
*/
@Query("Select a from JpaAction a join a.distributionSet ds join ds.modules modul where a.target.controllerId = :target and modul.id = :module order by a.id desc")
List<Action> findActionByTargetAndSoftwareModule(@Param("target") String targetId, @Param("module") Long moduleId);
/** /**
* Retrieves the latest finished {@link Action} for given target and {@link DistributionSet}. * Retrieves the latest finished {@link Action} for given target and {@link DistributionSet}.
@@ -205,55 +65,11 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
Optional<Action> findFirstByTargetIdAndDistributionSetIdAndStatusOrderByIdDesc(@Param("target") long targetId, Optional<Action> findFirstByTargetIdAndDistributionSetIdAndStatusOrderByIdDesc(@Param("target") long targetId,
@Param("ds") Long dsId, @Param("status") Action.Status status); @Param("ds") Long dsId, @Param("status") Action.Status status);
/**
* Retrieves all {@link Action}s which are referring the given
* {@link DistributionSet} and {@link Target}.
*
* @param pageable
* page parameters
* @param target
* is the assigned target
* @param ds
* the {@link DistributionSet} on which will be filtered
* @return the found {@link Action}s
*/
@Query("Select a from JpaAction a where a.target = :target and a.distributionSet = :ds")
Page<JpaAction> findByTargetAndDistributionSet(Pageable pageable, @Param("target") JpaTarget target,
@Param("ds") JpaDistributionSet ds);
/**
* Retrieves all {@link Action}s of a specific target, without pagination
* ordered by action ID.
*
* @param target
* to search for
* @return a list of actions according to the searched target
*/
@Query("Select a from JpaAction a where a.target = :target order by a.id")
List<JpaAction> findByTarget(@Param("target") Target target);
/**
* Retrieves all {@link Action}s of a specific target and given active flag
* ordered by action ID. Loads also the lazy {@link Action#getDistributionSet()}
* field.
*
* @param pageable
* page parameters
* @param controllerId
* to search for
* @param active
* {@code true} for all actions which are currently active,
* {@code false} for inactive
* @return a list of actions
*/
@EntityGraph(value = "Action.ds", type = EntityGraphType.LOAD)
@Query("Select a from JpaAction a where a.target.controllerId = :controllerId and a.active = :active")
Page<Action> findByActiveAndTarget(Pageable pageable, @Param("controllerId") String controllerId,
@Param("active") boolean active);
/** /**
* Switches the status of actions from one specific status into another, only if * Switches the status of actions from one specific status into another, only if
* the actions are in a specific status. This should be a atomar operation. * the actions are in a specific status. This should be a atomic operation.
* <p/>
* No access control applied
* *
* @param statusToSet * @param statusToSet
* the new status the actions should get * the new status the actions should get
@@ -270,27 +86,15 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
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); @Param("active") boolean active, @Param("currentStatus") Action.Status currentStatus);
/**
*
* Retrieves all active {@link Action}s by given controllerId filtered by a
* status
*
* @param controllerId
* the IDs of targets for the actions
* @param status
* the current status of the actions
* @return the found list of {@link Action}
*/
@Query("SELECT a FROM JpaAction a WHERE a.target.controllerId = :controllerId AND a.active = true AND a.status = :status")
List<JpaAction> findByTargetIdAndIsActiveAndActionStatus(@Param("controllerId") String controllerId,
@Param("status") Action.Status status);
/** /**
* *
* Retrieves all IDs for {@link Action}s referring to the given target IDs, * Retrieves all IDs for {@link Action}s referring to the given target IDs,
* active flag, current status and distribution set not requiring migration * active flag, current status and distribution set not requiring migration
* step. * step.
* <p/>
* No access control applied
* *
* @deprecated will be removed
* @param targetIds * @param targetIds
* the IDs of targets for the actions * the IDs of targets for the actions
* @param active * @param active
@@ -299,23 +103,12 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
* the current status of the actions * the current status of the actions
* @return the found list of {@link Action} IDs * @return the found list of {@link Action} IDs
*/ */
@Deprecated(forRemoval = true)
@Query("SELECT a.id FROM JpaAction a WHERE a.target IN :targetsIds AND a.active = :active AND a.status = :currentStatus AND a.distributionSet.requiredMigrationStep = false") @Query("SELECT a.id FROM JpaAction a WHERE a.target IN :targetsIds AND a.active = :active AND a.status = :currentStatus AND a.distributionSet.requiredMigrationStep = false")
List<Long> findByTargetIdInAndIsActiveAndActionStatusAndDistributionSetNotRequiredMigrationStep( List<Long> findByTargetIdInAndIsActiveAndActionStatusAndDistributionSetNotRequiredMigrationStep(
@Param("targetsIds") List<Long> targetIds, @Param("active") boolean active, @Param("targetsIds") List<Long> targetIds, @Param("active") boolean active,
@Param("currentStatus") Action.Status currentStatus); @Param("currentStatus") Action.Status currentStatus);
/**
* Retrieves all {@link Action}s that matches the queried externalRefs.
*
* @param externalRefs
* for which the actions need to be found
* @param active
* flag to indicate active/inactive actions
* @return list of actions
*/
List<Action> findByExternalRefInAndActive(@Param("externalRefs") List<String> externalRefs,
@Param("active") boolean active);
/** /**
* Retrieves an {@link Action} that matches the queried externalRef. * Retrieves an {@link Action} that matches the queried externalRef.
* *
@@ -328,7 +121,10 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
/** /**
* Switches the status of actions from one specific status into another for * Switches the status of actions from one specific status into another for
* given actions IDs, active flag and current status * given actions IDs, active flag and current status
* <p/>
* No access control applied
* *
* @deprecated will be removed
* @param statusToSet * @param statusToSet
* the new status the actions should get * the new status the actions should get
* @param actionIds * @param actionIds
@@ -339,6 +135,7 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
* the current status of the actions * the current status of the actions
* @return the amount of updated actions * @return the amount of updated actions
*/ */
@Deprecated(forRemoval = true)
@Modifying @Modifying
@Transactional @Transactional
@Query("UPDATE JpaAction a SET a.status = :statusToSet WHERE a.id IN :actionIds AND a.active = :active AND a.status = :currentStatus") @Query("UPDATE JpaAction a SET a.status = :statusToSet WHERE a.id IN :actionIds AND a.active = :active AND a.status = :currentStatus")
@@ -346,37 +143,10 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
@Param("actionIds") List<Long> actionIds, @Param("active") boolean active, @Param("actionIds") List<Long> actionIds, @Param("active") boolean active,
@Param("currentStatus") Action.Status currentStatus); @Param("currentStatus") Action.Status currentStatus);
/**
*
* Retrieves all {@link Action}s which are active and referring to the given
* target Ids and distribution set not requiring migration step.
*
* @param targetIds
* the IDs of targets for the actions
* @param notStatus
* the status which the actions should not have
* @return the found list of {@link Action}s
*/
@EntityGraph(attributePaths = { "target" }, type = EntityGraphType.LOAD)
@Query("SELECT a FROM JpaAction a WHERE a.active = true AND a.distributionSet.requiredMigrationStep = false AND a.target IN ?1 AND a.status != ?2")
List<JpaAction> findByActiveAndTargetIdInAndActionStatusNotEqualToAndDistributionSetNotRequiredMigrationStep(
Collection<Long> targetIds, Action.Status notStatus);
/**
*
* Retrieves all {@link Action}s which are active and referring to the given
* target Ids and distribution set not requiring migration step.
*
* @param targetIds
* the IDs of targets for the actions
* @return the found list of {@link Action}s
*/
@EntityGraph(attributePaths = { "target" }, type = EntityGraphType.LOAD)
@Query("SELECT a FROM JpaAction a WHERE a.active = true AND a.distributionSet.requiredMigrationStep = false AND a.target IN ?1")
List<JpaAction> findByActiveAndTargetIdInAndDistributionSetNotRequiredMigrationStep(Collection<Long> targetIds);
/** /**
* Counts all {@link Action}s referring to the given target. * Counts all {@link Action}s referring to the given target.
* <p/>
* No access control applied
* *
* @param controllerId * @param controllerId
* the target to count the {@link Action}s * the target to count the {@link Action}s
@@ -386,6 +156,8 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
/** /**
* Counts all {@link Action}s referring to the given targetId. * Counts all {@link Action}s referring to the given targetId.
* <p/>
* No access control applied
* *
* @param targetId * @param targetId
* the target to count the {@link Action}s * the target to count the {@link Action}s
@@ -395,6 +167,8 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
/** /**
* Counts all {@link Action}s referring to the given DistributionSet. * Counts all {@link Action}s referring to the given DistributionSet.
* <p/>
* No access control applied
* *
* @param distributionSet * @param distributionSet
* DistributionSet to count the {@link Action}s from * DistributionSet to count the {@link Action}s from
@@ -404,6 +178,8 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
/** /**
* Counts all active {@link Action}s referring to the given DistributionSet. * Counts all active {@link Action}s referring to the given DistributionSet.
* <p/>
* No access control applied
* *
* @param distributionSet * @param distributionSet
* DistributionSet to count the {@link Action}s from * DistributionSet to count the {@link Action}s from
@@ -414,6 +190,8 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
/** /**
* Counts all active {@link Action}s referring to the given DistributionSet * Counts all active {@link Action}s referring to the given DistributionSet
* that are not in a given state. * that are not in a given state.
* <p/>
* No access control applied
* *
* @param distributionSet * @param distributionSet
* DistributionSet to count the {@link Action}s from * DistributionSet to count the {@link Action}s from
@@ -428,6 +206,8 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
* are currently not in the given status. An in-clause statement does not * 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 * 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. * rollout-management to find out actions which are not in specific states.
* <p/>
* No access control applied
* *
* @param rollout * @param rollout
* the rollout the actions are belong to * the rollout the actions are belong to
@@ -443,6 +223,8 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
/** /**
* Counts all actions referring to a given rollout and rolloutgroup. * Counts all actions referring to a given rollout and rolloutgroup.
* <p/>
* No access control applied
* *
* @param rollout * @param rollout
* the rollout the actions belong to * the rollout the actions belong to
@@ -454,6 +236,8 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
/** /**
* Counts all actions referring to a given rollout, rolloutgroup and status. * Counts all actions referring to a given rollout, rolloutgroup and status.
* <p/>
* No access control applied
* *
* @param rolloutId * @param rolloutId
* the ID of rollout the actions belong to * the ID of rollout the actions belong to
@@ -468,6 +252,8 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
/** /**
* Counts all actions referring to a given rollout and status. * Counts all actions referring to a given rollout and status.
* <p/>
* No access control applied
* *
* @param rolloutId * @param rolloutId
* the ID of the rollout the actions belong to * the ID of the rollout the actions belong to
@@ -481,6 +267,8 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
/** /**
* Returns {@code true} if actions for the given rollout exists, otherwise * Returns {@code true} if actions for the given rollout exists, otherwise
* {@code false} * {@code false}
* <p/>
* No access control applied
* *
* @param rolloutId * @param rolloutId
* the ID of the rollout the actions belong to * the ID of the rollout the actions belong to
@@ -493,6 +281,8 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
/** /**
* Returns {@code true} if actions for the given rollout exists, otherwise * Returns {@code true} if actions for the given rollout exists, otherwise
* {@code false} * {@code false}
* <p/>
* No access control applied
* *
* @param rolloutId * @param rolloutId
* the ID of the rollout the actions belong to * the ID of the rollout the actions belong to
@@ -507,8 +297,10 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
/** /**
* Retrieving all actions referring to a given rollout with a specific action as * Retrieving all actions referring to a given rollout with a specific action as
* parent reference and a specific status. * parent reference and a specific status.
* * <p/>
* Finding all actions of a specific rolloutgroup parent relation. * Finding all actions of a specific rolloutgroup parent relation.
* <p/>
* No access control applied
* *
* @param pageable * @param pageable
* page parameters * page parameters
@@ -527,6 +319,8 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
/** /**
* Retrieving all actions referring to the first group of a rollout. * Retrieving all actions referring to the first group of a rollout.
* <p/>
* No access control applied
* *
* @param pageable * @param pageable
* page parameters * page parameters
@@ -544,6 +338,8 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
/** /**
* Retrieves all actions for a specific rollout and in a specific status. * Retrieves all actions for a specific rollout and in a specific status.
* <p/>
* No access control applied
* *
* @param pageable * @param pageable
* page parameters * page parameters
@@ -558,6 +354,8 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
/** /**
* Get list of objects which has details of status and count of targets in * Get list of objects which has details of status and count of targets in
* each status in specified rollout. * each status in specified rollout.
* <p/>
* No access control applied
* *
* @param rolloutId * @param rolloutId
* id of {@link Rollout} * id of {@link Rollout}
@@ -569,6 +367,8 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
/** /**
* Get list of objects which has details of status and count of targets in * Get list of objects which has details of status and count of targets in
* each status in specified rollout. * each status in specified rollout.
* <p/>
* No access control applied
* *
* @param rolloutId * @param rolloutId
* id of {@link Rollout} * id of {@link Rollout}
@@ -580,6 +380,8 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
/** /**
* Get list of objects which has details of status and count of targets in * Get list of objects which has details of status and count of targets in
* each status in specified rollout group. * each status in specified rollout group.
* <p/>
* No access control applied
* *
* @param rolloutGroupId * @param rolloutGroupId
* id of {@link RolloutGroup} * id of {@link RolloutGroup}
@@ -591,6 +393,8 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
/** /**
* Get list of objects which has details of status and count of targets in * Get list of objects which has details of status and count of targets in
* each status in specified rollout group. * each status in specified rollout group.
* <p/>
* No access control applied
* *
* @param rolloutGroupId * @param rolloutGroupId
* list of id of {@link RolloutGroup} * list of id of {@link RolloutGroup}
@@ -599,18 +403,6 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
@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") @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> getStatusCountByRolloutGroupId(List<Long> rolloutGroupId);
/**
* Deletes all actions with the given IDs.
*
* @param actionIDs
* the IDs of the actions to be deleted.
*/
@Modifying
@Transactional
// 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);
/** /**
* Updates the externalRef of an action by its actionId. * Updates the externalRef of an action by its actionId.
* *
@@ -623,4 +415,16 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
@Transactional @Transactional
@Query("UPDATE JpaAction a SET a.externalRef = :externalRef WHERE a.id = :actionId") @Query("UPDATE JpaAction a SET a.externalRef = :externalRef WHERE a.id = :actionId")
void updateExternalRef(@Param("actionId") Long actionId, @Param("externalRef") String externalRef); void updateExternalRef(@Param("actionId") Long actionId, @Param("externalRef") String externalRef);
/**
* Deletes all actions with the given IDs.
*
* @param actionIDs
* the IDs of the actions to be deleted.
*/
@Modifying
@Transactional
// 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);
} }

View File

@@ -7,17 +7,13 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.repository;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus; import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.ActionStatus; import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@@ -27,25 +23,15 @@ import org.springframework.transaction.annotation.Transactional;
* *
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true)
public interface ActionStatusRepository public interface ActionStatusRepository extends BaseEntityRepository<JpaActionStatus> {
extends BaseEntityRepository<JpaActionStatus, Long>, JpaSpecificationExecutor<JpaActionStatus> {
/** /**
* Counts {@link ActionStatus} entries of given {@link Action} in * Counts {@link ActionStatus} entries of given {@link Action} in
* repository. * repository.
* <p/>
* No access control applied
* *
* @param action * @param actionId of the action to count status entries for
* to count status entries
* @return number of actions in repository
*/
Long countByAction(JpaAction action);
/**
* Counts {@link ActionStatus} entries of given {@link Action} in
* repository.
*
* @param actionId
* of the action to count status entries for
* @return number of actions in repository * @return number of actions in repository
*/ */
long countByActionId(Long actionId); long countByActionId(Long actionId);
@@ -53,32 +39,19 @@ public interface ActionStatusRepository
/** /**
* Retrieves all {@link ActionStatus} entries from repository of given * Retrieves all {@link ActionStatus} entries from repository of given
* ActionId. * ActionId.
* <p/>
* No access control applied
* *
* @param pageReq * @param pageReq parameters
* parameters * @param actionId of the status entries
* @param actionId
* of the status entries
* @return pages list of {@link ActionStatus} entries * @return pages list of {@link ActionStatus} entries
*/ */
Page<ActionStatus> findByActionId(Pageable pageReq, Long actionId); Page<ActionStatus> findByActionId(Pageable pageReq, Long actionId);
/**
* Finds all status updates for the defined action and target including
* {@link ActionStatus#getMessages()}.
*
* @param pageReq
* for page configuration
* @param target
* to look for
* @param actionId
* to look for
* @return Page with found targets
*/
@EntityGraph(value = "ActionStatus.withMessages", type = EntityGraphType.LOAD)
Page<ActionStatus> getByActionId(Pageable pageReq, Long actionId);
/** /**
* Finds a filtered list of status messages for an action. * Finds a filtered list of status messages for an action.
* <p/>
* No access control applied
* *
* @param pageable * @param pageable
* for page configuration * for page configuration

View File

@@ -0,0 +1,147 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.repository;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity_;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaTenantAwareBaseEntity;
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.lang.Nullable;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityManager;
/**
* Command repository operations for all {@link TenantAwareBaseEntity}s.
*
* @param <T>
* type if the entity type
*/
@NoRepositoryBean
@Transactional(readOnly = true)
public interface BaseEntityRepository<T extends AbstractJpaTenantAwareBaseEntity>
extends PagingAndSortingRepository<T, Long>, CrudRepository<T, Long>, JpaSpecificationExecutor<T>,
NoCountSliceRepository<T>, ACMRepository<T> {
/**
* Overrides
* {@link org.springframework.data.repository.CrudRepository#findAll()}
* to return a list of found entities instead of an instance of
* {@link Iterable} to be able to work with it directly in further code
* processing instead of converting the {@link Iterable}.
*
* @return the found entities
*/
@Override
List<T> findAll();
/**
* Overrides
* {@link org.springframework.data.repository.CrudRepository#findAllById(Iterable)}
* to return a list of found entities instead of an instance of
* {@link Iterable} to be able to work with it directly in further code
* processing instead of converting the {@link Iterable}.
*
* @param ids to search in the database for
* @return the found entities
*/
@Override
List<T> findAllById(final Iterable<Long> ids);
/**
* Overrides
* {@link org.springframework.data.repository.CrudRepository#saveAll(Iterable)}
* to return a list of created entities instead of an instance of
* {@link Iterable} to be able to work with it directly in further code
* processing instead of converting the {@link Iterable}.
*
* @param entities to persist in the database
* @return the created entities
*/
@Override
@Transactional
<S extends T> List<S> saveAll(Iterable<S> entities);
// TODO When we switch to Spring 3.0 probably we could remove extending methods using
// queries and make here a default implementation using JPASpecificationExecutor delete method
// TODO To be considered if this method is needed at all
/**
* Deletes all entities of a given tenant from this repository. For safety
* reasons (this is a "delete everything" query after all) we add the tenant
* manually to query even if this will be done by {@link EntityManager}
* anyhow. The DB should take care of optimizing this away.
* <p/>
*
* @param tenant to delete data from
*/
void deleteByTenant(String tenant);
/**
* Returns a wrapper (or the same instance if access controller is <code>null</code> of this repository that
* supports ACM.
* <p/>
* Note: To use ACM support the returned object shall be used! <code>this</code> object will not achieve ACM
* support!
* <p/>
* Notes on ACM support (if enabled, i.e. <code>accessController</code> is not <code>null</code>):
* <ul>
* <li>ACM is applied for all {@link BaseEntityRepository} methods</li>
* <li>ACM is applied for all <code>findXXX</code> methods that returns {@link Iterable}
* (expected of <code>? extends T</code>),
* <code>? extends T</code> or {@link java.util.Optional}(expected of <code>? extends T</code>).
* </li>
* <li>For all other methods defined on repository interface level that are implemented, for instance,
* via {@link org.springframework.data.jpa.repository.Query} or using naming conventions
* (existsBySomething) the ACM won't be applied!
* </li>
* </ul>
*
* @param accessController access controller to be applied to the result
* @param entityType the entity type of the repository
* @return a repository that supports ACM.
*/
default BaseEntityRepository<T> withACM(@Nullable final AccessController<T> accessController) {
if (accessController == null) {
return this;
} else {
return BaseEntityRepositoryACM.of(this, accessController);
}
}
default <T extends AbstractJpaTenantAwareBaseEntity> Specification<T> byIdSpec(final Long id) {
return (root, query, cb) -> cb.equal(root.get(AbstractJpaBaseEntity_.id), id);
}
default <T extends AbstractJpaTenantAwareBaseEntity> Specification<T> byIdsSpec(final Iterable<Long> ids) {
final Collection<Long> collection;
if (ids instanceof Collection<Long>) {
collection = (Collection<Long>) ids;
} else {
collection = new LinkedList<>();
ids.forEach(collection::add);
}
return (root, query, cb) -> root.get(AbstractJpaBaseEntity_.id).in(collection);
}
default Optional<AccessController<T>> getAccessController() {
return Optional.empty();
}
}

View File

@@ -0,0 +1,345 @@
/**
* Copyright (c) 2023 Bosch.IO GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.repository;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaTenantAwareBaseEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import javax.transaction.Transactional;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
public class BaseEntityRepositoryACM<T extends AbstractJpaTenantAwareBaseEntity> implements BaseEntityRepository<T> {
private static final Logger LOGGER = LoggerFactory.getLogger(BaseEntityRepositoryACM.class);
private final BaseEntityRepository<T> repository;
private final AccessController<T> accessController;
BaseEntityRepositoryACM(final BaseEntityRepository<T> repository, final AccessController<T> accessController) {
this.repository = repository;
this.accessController = accessController;
}
@SuppressWarnings("unchecked")
static <T extends AbstractJpaTenantAwareBaseEntity, R extends BaseEntityRepository<T>> R of(
final R repository, @NonNull final AccessController<T> accessController) {
Objects.requireNonNull(repository);
Objects.requireNonNull(accessController);
final BaseEntityRepositoryACM<T> repositoryACM =
new BaseEntityRepositoryACM<>(repository, accessController);
final R acmProxy = (R) Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
repository.getClass().getInterfaces(),
(proxy, method, args) -> {
try {
try {
// TODO - cache mapping so to speed things
final Method delegateMethod =
BaseEntityRepositoryACM.class.getDeclaredMethod(
method.getName(), method.getParameterTypes());
return delegateMethod.invoke(repositoryACM, args);
} catch (final NoSuchMethodException e) {
// call to repository itself
}
if (method.getName().startsWith("find") || method.getName().startsWith("get")) {
final Object result = method.invoke(repository, args);
if (Iterable.class.isAssignableFrom(method.getReturnType())) {
for (final T e : ((Iterable<T>) result)) {
accessController.assertOperationAllowed(AccessController.Operation.READ, e);
}
} else if (Optional.class.isAssignableFrom(method.getReturnType())) {
return ((Optional<T>)result).filter(t -> isOperationAllowed(AccessController.Operation.READ, t, accessController));
} else if (repository.getDomainClass().isAssignableFrom(method.getReturnType())) {
accessController.assertOperationAllowed(AccessController.Operation.READ, (T)result);
}
return result;
} else if ("toString".equals(method.getName()) && method.getParameterCount() == 0) {
return BaseEntityRepositoryACM.class.getSimpleName() +
"(repository: " + repository + ", accessController: " + accessController + ")";
} else {
return method.invoke(repository, args);
}
} catch (final InvocationTargetException e) {
throw e.getCause() == null ? e : e.getCause();
}
});
LOGGER.info("Proxy created -> {}", acmProxy);
return acmProxy;
}
@Override
@NonNull
public Optional<T> findById(@NonNull final Long id) {
return findOne(byIdSpec(id));
}
@Override
@NonNull
public List<T> findAll() {
return findAll((Specification<T>) null);
}
@Override
@NonNull
public List<T> findAllById(@NonNull final Iterable<Long> ids) {
return findAll(byIdsSpec(ids));
}
@Override
public boolean existsById(@NonNull final Long id) {
return exists(byIdSpec(id));
}
@Override
public long count() {
return count(null);
}
@Override
public void delete(@NonNull final T entity) {
accessController.assertOperationAllowed(AccessController.Operation.DELETE, entity);
repository.delete(entity);
}
@Override
public void deleteById(@NonNull final Long id) {
if (!exists(AccessController.Operation.READ, byIdSpec(id))) {
throw new EntityNotFoundException(repository.getDomainClass(), id);
}
if (!exists(AccessController.Operation.DELETE, byIdSpec(id))) {
throw new InsufficientPermissionException();
}
repository.deleteById(id);
}
@Override
public void deleteAllById(@NonNull final Iterable<? extends Long> ids) {
final Set<Long> idList = toSetDistinct(ids);
if (count(AccessController.Operation.DELETE, byIdsSpec(idList)) != idList.size()) {
throw new InsufficientPermissionException("Has at least one id that is not allowed for deletion!");
}
repository.deleteAllById(idList);
}
@Override
public void deleteAll(@NonNull final Iterable<? extends T> entities) {
accessController.assertOperationAllowed(AccessController.Operation.DELETE, entities);
repository.deleteAll(entities);
}
@Override
public void deleteAll() {
// TODO - shall we remove deleteByTenant and implement this method instead?
// if (accessController.getAccessRules(AccessController.Operation.DELETE).isPresent()) {
// throw new InsufficientPermissionException(
// "DELETE operation has restriction for given context! deleteAll can't be executed!");
// }
// repository.deleteAll();
throw new UnsupportedOperationException();
}
@Override
@NonNull
public <S extends T> S save(@NonNull final S entity) {
accessController.assertOperationAllowed(AccessController.Operation.UPDATE, entity);
return repository.save(entity);
}
@Override
public <S extends T> List<S> saveAll(final Iterable<S> entities) {
accessController.assertOperationAllowed(AccessController.Operation.UPDATE, entities);
return repository.saveAll(entities);
}
@Override
@NonNull
public Optional<T> findOne(final Specification<T> spec) {
return repository.findOne(accessController.appendAccessRules(AccessController.Operation.READ, spec));
}
@Override
@NonNull
public Iterable<T> findAll(@NonNull final Sort sort) {
return findAll(null, sort);
}
@Override
@NonNull
public Page<T> findAll(@NonNull final Pageable pageable) {
return findAll(null, pageable);
}
@Override
@NonNull
public List<T> findAll(final Specification<T> spec) {
return repository.findAll(accessController.appendAccessRules(AccessController.Operation.READ, spec));
}
@Override
@NonNull
public Page<T> findAll(final Specification<T> spec, @NonNull final Pageable pageable) {
return repository.findAll(accessController.appendAccessRules(AccessController.Operation.READ, spec), pageable);
}
@Override
@NonNull
public List<T> findAll(final Specification<T> spec, @NonNull final Sort sort) {
return repository.findAll(accessController.appendAccessRules(AccessController.Operation.READ, spec), sort);
}
@Override
public boolean exists(@NonNull final Specification<T> spec) {
return repository.exists(
Objects.requireNonNull(accessController.appendAccessRules(AccessController.Operation.READ, spec)));
}
@Override
public long count(final Specification<T> spec) {
return repository.count(accessController.appendAccessRules(AccessController.Operation.READ, spec));
}
@Override
public Slice<T> findAllWithoutCount(final Pageable pageable) {
return findAllWithoutCount(null, pageable);
}
@Override
public Slice<T> findAllWithoutCount(final Specification<T> spec, final Pageable pageable) {
return repository.findAllWithoutCount(
accessController.appendAccessRules(AccessController.Operation.READ, spec), pageable);
}
@Override
@Transactional
@NonNull
public <S extends T> S save(@Nullable AccessController.Operation operation, @NonNull final S entity) {
if (operation != null) {
accessController.assertOperationAllowed(operation, entity);
}
return repository.save(entity);
}
@Override
@Transactional
public <S extends T> List<S> saveAll(@Nullable AccessController.Operation operation, final Iterable<S> entities) {
if (operation != null) {
accessController.assertOperationAllowed(operation, entities);
}
return repository.saveAll(entities);
}
@NonNull
public Optional<T> findOne(@Nullable AccessController.Operation operation, @Nullable Specification<T> spec) {
if (operation == null) {
return repository.findOne(spec);
} else {
return repository.findOne(accessController.appendAccessRules(operation, spec));
}
}
@Override
@NonNull
public List<T> findAll(@Nullable final AccessController.Operation operation, @Nullable final Specification<T> spec) {
if (operation == null) {
return repository.findAll(spec);
} else {
return repository.findAll(accessController.appendAccessRules(operation, spec));
}
}
@Override
@NonNull
public boolean exists(@Nullable AccessController.Operation operation, Specification<T> spec) {
if (operation == null) {
return repository.exists(spec);
} else {
return repository.exists(
Objects.requireNonNull(accessController.appendAccessRules(operation, spec)));
}
}
@Override
@NonNull
public long count(@Nullable final AccessController.Operation operation, @Nullable final Specification<T> spec) {
if (operation == null) {
return repository.count(spec);
} else {
return repository.count(accessController.appendAccessRules(operation, spec));
}
}
@Override
@NonNull
public Slice<T> findAllWithoutCount(
@Nullable final AccessController.Operation operation, @Nullable Specification<T> spec, Pageable pageable) {
if (operation == null) {
return repository.findAllWithoutCount(spec, pageable);
} else {
return repository.findAllWithoutCount(accessController.appendAccessRules(operation, spec), pageable);
}
}
@Override
@NonNull
public Class<T> getDomainClass() {
return repository.getDomainClass();
}
@Override
public Optional<AccessController<T>> getAccessController() {
return Optional.of(accessController);
}
@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!");
}
repository.deleteByTenant(tenant);
}
private static <T> boolean isOperationAllowed(
final AccessController.Operation operation, T entity,
final AccessController<T> accessController) {
try {
accessController.assertOperationAllowed(operation, entity);
return true;
} catch (final InsufficientPermissionException e) {
return false;
}
}
@SuppressWarnings({"rawtypes", "unchecked"})
private static <T extends Long> Set<Long> toSetDistinct(final Iterable<T> i) {
final Set<Long> set = new HashSet<>();
i.forEach(set::add);
return set;
}
}

View File

@@ -7,7 +7,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.repository;
import org.eclipse.hawkbit.repository.jpa.model.DsMetadataCompositeKey; import org.eclipse.hawkbit.repository.jpa.model.DsMetadataCompositeKey;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetMetadata; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetMetadata;
@@ -29,12 +29,11 @@ public interface DistributionSetMetadataRepository
/** /**
* Counts the meta data entries that match the given distribution set ID. * Counts the meta data entries that match the given distribution set ID.
* <p/>
* No access control applied
* *
* @param id * @param id of the distribution set.
* of the distribution set.
*
* @return The number of matching meta data entries. * @return The number of matching meta data entries.
*/ */
long countByDistributionSetId(@Param("id") Long id); long countByDistributionSetId(@Param("id") Long id);
} }

View File

@@ -7,7 +7,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.repository;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@@ -21,11 +21,7 @@ import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Rollout; import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Tag;
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity; import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
@@ -37,34 +33,23 @@ import org.springframework.transaction.annotation.Transactional;
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true)
public interface DistributionSetRepository public interface DistributionSetRepository
extends BaseEntityRepository<JpaDistributionSet, Long>, JpaSpecificationExecutor<JpaDistributionSet> { extends BaseEntityRepository<JpaDistributionSet> {
/**
* Finds {@link DistributionSet}s by assigned {@link Tag}.
*
* @param pageable
* paging and sorting information
*
* @param tagId
* to be found
* @return list of found {@link DistributionSet}s
*/
@Query(value = "Select Distinct ds from JpaDistributionSet ds join ds.tags dst where dst.id = :tag and ds.deleted = 0")
Page<JpaDistributionSet> findByTag(Pageable pageable, @Param("tag") Long tagId);
/** /**
* Count {@link Rollout}s by Status for Distribution set. * Count {@link Rollout}s by Status for Distribution set.
* <p/>
* No access control applied.
* *
* @param dsId * @param dsId to be found
* to be found
* @return map for {@link Rollout}s status counts * @return map for {@link Rollout}s status counts
*/ */
@Query(value = "SELECT r.status as name, COUNT(r.status) as data FROM JpaRollout r WHERE r.distributionSet.id = :dsId GROUP BY r.status") @Query(value = "SELECT r.status as name, COUNT(r.status) as data FROM JpaRollout r WHERE r.distributionSet.id = :dsId GROUP BY r.status")
List<JpaStatistic> countRolloutsByStatusForDistributionSet(@Param("dsId") Long dsId); List<JpaStatistic> countRolloutsByStatusForDistributionSet(@Param("dsId") Long dsId);
/** /**
* Count {@link Action}s by Status for Distribution set. * Count {@link Action}s by Status for Distribution set.
* <p/>
* No access control applied.
* *
* @param dsId * @param dsId
* to be found * to be found
@@ -75,6 +60,8 @@ public interface DistributionSetRepository
/** /**
* Count total AutoAssignments for Distribution set. * Count total AutoAssignments for Distribution set.
* <p/>
* No access control applied.
* *
* @param dsId * @param dsId
* to be found * to be found
@@ -85,6 +72,8 @@ public interface DistributionSetRepository
/** /**
* deletes the {@link DistributionSet}s with the given IDs. * deletes the {@link DistributionSet}s with the given IDs.
* <p/>
* No access control applied.
* *
* @param ids * @param ids
* to be deleted * to be deleted
@@ -94,22 +83,11 @@ public interface DistributionSetRepository
@Query("update JpaDistributionSet d set d.deleted = 1 where d.id in :ids") @Query("update JpaDistributionSet d set d.deleted = 1 where d.id in :ids")
void deleteDistributionSet(@Param("ids") Long... ids); void deleteDistributionSet(@Param("ids") Long... ids);
/**
* deletes {@link DistributionSet}s by the given IDs.
*
* @param ids
* List of IDs of {@link DistributionSet}s to be deleted
* @return number of affected/deleted records
*/
@Modifying
@Transactional
// Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477
@Query("DELETE FROM JpaDistributionSet d WHERE d.id IN ?1")
int deleteByIdIn(Collection<Long> ids);
/** /**
* Finds {@link DistributionSet}s where given {@link SoftwareModule} is * Finds {@link DistributionSet}s where given {@link SoftwareModule} is
* assigned. * assigned.
* <p/>
* No access control applied.
* *
* @param moduleId * @param moduleId
* to search for * to search for
@@ -120,6 +98,8 @@ public interface DistributionSetRepository
/** /**
* Finds {@link DistributionSet}s based on given ID that are assigned yet to * Finds {@link DistributionSet}s based on given ID that are assigned yet to
* an {@link Action}, i.e. in use. * an {@link Action}, i.e. in use.
* <p/>
* No access control applied.
* *
* @param ids * @param ids
* to search for * to search for
@@ -131,6 +111,8 @@ public interface DistributionSetRepository
/** /**
* Finds {@link DistributionSet}s based on given ID that are assigned yet to * Finds {@link DistributionSet}s based on given ID that are assigned yet to
* an {@link Rollout}, i.e. in use. * an {@link Rollout}, i.e. in use.
* <p/>
* No access control applied.
* *
* @param ids * @param ids
* to search for * to search for
@@ -139,16 +121,6 @@ public interface DistributionSetRepository
@Query("select ra.distributionSet.id from JpaRollout ra where ra.distributionSet.id in :ids") @Query("select ra.distributionSet.id from JpaRollout ra where ra.distributionSet.id in :ids")
List<Long> findAssignedToRolloutDistributionSetsById(@Param("ids") Collection<Long> ids); List<Long> findAssignedToRolloutDistributionSetsById(@Param("ids") Collection<Long> ids);
/**
* Finds the distribution set for a specific action.
*
* @param action
* the action associated with the distribution set to find
* @return the distribution set associated with the given action
*/
@Query("select DISTINCT d from JpaDistributionSet d join fetch d.modules m join d.actions a where a.id = :action")
JpaDistributionSet findByActionId(@Param("action") Long action);
@Query("select DISTINCT ds from JpaDistributionSet ds join fetch ds.modules join ds.assignedToTargets t where t.controllerId = :controllerId") @Query("select DISTINCT ds from JpaDistributionSet ds join fetch ds.modules join ds.assignedToTargets t where t.controllerId = :controllerId")
Optional<DistributionSet> findAssignedToTarget(@Param("controllerId") String controllerId); Optional<DistributionSet> findAssignedToTarget(@Param("controllerId") String controllerId);
@@ -157,6 +129,8 @@ public interface DistributionSetRepository
/** /**
* Counts {@link DistributionSet} instances of given type in the repository. * Counts {@link DistributionSet} instances of given type in the repository.
* <p/>
* No access control applied.
* *
* @param typeId * @param typeId
* to search for * to search for
@@ -164,24 +138,6 @@ public interface DistributionSetRepository
*/ */
long countByTypeId(Long typeId); long countByTypeId(Long typeId);
/**
* Counts {@link DistributionSet} with given
* {@link DistributionSet#getName()} and
* {@link DistributionSet#getVersion()}.
*
* @param name
* to search for
* @param version
* to search for
* @return number of found {@link DistributionSet}s
*/
long countByNameAndVersion(String name, String version);
@Override
// Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477
@Query("SELECT d FROM JpaDistributionSet d WHERE d.id IN ?1")
List<JpaDistributionSet> findAllById(Iterable<Long> ids);
/** /**
* Deletes all {@link TenantAwareBaseEntity} of a given tenant. For safety * Deletes all {@link TenantAwareBaseEntity} of a given tenant. For safety
* reasons (this is a "delete everything" query after all) we add the tenant * reasons (this is a "delete everything" query after all) we add the tenant
@@ -195,5 +151,4 @@ public interface DistributionSetRepository
@Transactional @Transactional
@Query("DELETE FROM JpaDistributionSet t WHERE t.tenant = :tenant") @Query("DELETE FROM JpaDistributionSet t WHERE t.tenant = :tenant")
void deleteByTenant(@Param("tenant") String tenant); void deleteByTenant(@Param("tenant") String tenant);
} }

View File

@@ -7,7 +7,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.repository;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@@ -19,7 +19,6 @@ import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetTag; import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.TargetTag; import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity; import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
@@ -31,17 +30,7 @@ import org.springframework.transaction.annotation.Transactional;
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true)
public interface DistributionSetTagRepository public interface DistributionSetTagRepository
extends BaseEntityRepository<JpaDistributionSetTag, Long>, JpaSpecificationExecutor<JpaDistributionSetTag> { extends BaseEntityRepository<JpaDistributionSetTag> {
/**
* deletes the {@link DistributionSet} with the given name.
*
* @param tagName
* to be deleted
* @return 1 if tag was deleted
*/
@Modifying
@Transactional
Long deleteByName(String tagName);
/** /**
* find {@link DistributionSetTag} by its name. * find {@link DistributionSetTag} by its name.
@@ -52,16 +41,6 @@ public interface DistributionSetTagRepository
*/ */
Optional<DistributionSetTag> findByNameEquals(String tagName); Optional<DistributionSetTag> findByNameEquals(String tagName);
/**
* Checks if tag with given name exists.
*
* @param tagName
* to check for
* @return <code>true</code> is tag with given name exists
*/
@Query("SELECT CASE WHEN COUNT(t)>0 THEN 'true' ELSE 'false' END FROM JpaDistributionSetTag t WHERE t.name=:tagName")
boolean existsByName(@Param("tagName") String tagName);
/** /**
* Returns all instances of the type. * Returns all instances of the type.
* *
@@ -83,9 +62,4 @@ public interface DistributionSetTagRepository
@Transactional @Transactional
@Query("DELETE FROM JpaDistributionSetTag t WHERE t.tenant = :tenant") @Query("DELETE FROM JpaDistributionSetTag t WHERE t.tenant = :tenant")
void deleteByTenant(@Param("tenant") String tenant); void deleteByTenant(@Param("tenant") String tenant);
@Override
// Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477
@Query("SELECT d FROM JpaDistributionSetTag d WHERE d.id IN ?1")
List<JpaDistributionSetTag> findAllById(Iterable<Long> ids);
} }

View File

@@ -7,7 +7,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.repository;
import java.util.List; import java.util.List;
@@ -18,9 +18,6 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity; import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.PagingAndSortingRepository;
@@ -34,32 +31,13 @@ import org.springframework.transaction.annotation.Transactional;
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true)
public interface DistributionSetTypeRepository public interface DistributionSetTypeRepository
extends BaseEntityRepository<JpaDistributionSetType, Long>, JpaSpecificationExecutor<JpaDistributionSetType> { extends BaseEntityRepository<JpaDistributionSetType> {
/**
*
* @param pageable
* page parameters
* @param isDeleted
* to <code>true</code> if only soft deleted entries of
* <code>false</code> if undeleted ones
*
* @return list of found {@link DistributionSetType}s
*/
Page<JpaDistributionSetType> findByDeleted(Pageable pageable, boolean isDeleted);
/**
* @param isDeleted
* to <code>true</code> if only marked as deleted have to be
* counted or all undeleted.
*
* @return number of {@link DistributionSetType}s in the repository.
*/
long countByDeleted(boolean isDeleted);
/** /**
* Counts all distribution set type where a specific software module type is * Counts all distribution set type where a specific software module type is
* assigned to. * assigned to.
* <p/>
* No access control applied
* *
* @param softwareModuleType * @param softwareModuleType
* the software module type to count the distribution set type * the software module type to count the distribution set type
@@ -76,30 +54,18 @@ public interface DistributionSetTypeRepository
* manually to query even if this will by done by {@link EntityManager} * manually to query even if this will by done by {@link EntityManager}
* anyhow. The DB should take care of optimizing this away. * anyhow. The DB should take care of optimizing this away.
* *
* @param tenant * @param tenant to delete data from
* to delete data from
*/ */
@Modifying @Modifying
@Transactional @Transactional
@Query("DELETE FROM JpaDistributionSetType t WHERE t.tenant = :tenant") @Query("DELETE FROM JpaDistributionSetType t WHERE t.tenant = :tenant")
void deleteByTenant(@Param("tenant") String tenant); void deleteByTenant(@Param("tenant") String tenant);
/**
* Retrieves the {@link DistributionSetType}s for the given IDs. Workaround
* for https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477
*
* @param ids
* of the types to be located
*
* @return a list of distribution set types
*/
@Override
@Query("SELECT d FROM JpaDistributionSetType d WHERE d.id IN ?1")
List<JpaDistributionSetType> findAllById(Iterable<Long> ids);
/** /**
* Counts the {@link SoftwareModuleType}s which are associated with the * Counts the {@link SoftwareModuleType}s which are associated with the
* addressed {@link DistributionSetType}. * addressed {@link DistributionSetType}.
* <p/>
* No access control applied
* *
* @param id * @param id
* of the distribution set type * of the distribution set type
@@ -108,5 +74,4 @@ public interface DistributionSetTypeRepository
*/ */
@Query("SELECT COUNT (e.smType) FROM DistributionSetTypeElement e WHERE e.dsType.id = :id") @Query("SELECT COUNT (e.smType) FROM DistributionSetTypeElement e WHERE e.dsType.id = :id")
long countSmTypesById(@Param("id") Long id); long countSmTypesById(@Param("id") Long id);
} }

View File

@@ -0,0 +1,134 @@
/**
* Copyright (c) 2023 Bosch.IO GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.repository;
import java.io.Serializable;
import java.util.List;
import java.util.Optional;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import javax.transaction.Transactional;
import org.eclipse.hawkbit.repository.BaseRepositoryTypeProvider;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
/**
* Repository implementation that allows findAll with disabled count query.
*
* @param <T> the domain type the repository manages
* @param <ID> the type of the id of the entity the repository manages
*/
public class HawkBitBaseRepository<T, ID extends Serializable> extends SimpleJpaRepository<T, ID>
implements NoCountSliceRepository<T>, ACMRepository<T> {
public HawkBitBaseRepository(final Class<T> domainClass, final EntityManager em) {
super(domainClass, em);
}
public HawkBitBaseRepository(final JpaEntityInformation<T, ?> entityInformation, final EntityManager entityManager) {
super(entityInformation, entityManager);
}
@Override
public Slice<T> findAllWithoutCount(@Nullable final Specification<T> spec, final Pageable pageable) {
final TypedQuery<T> query = getQuery(spec, pageable);
return pageable.isUnpaged() ? new PageImpl<>(query.getResultList()) : readPageWithoutCount(query, pageable);
}
@Override
public Slice<T> findAllWithoutCount(final Pageable pageable) {
return findAllWithoutCount(null, pageable);
}
@Override
@Transactional
@NonNull
public <S extends T> S save(@Nullable AccessController.Operation operation, @NonNull final S entity) {
return save(entity);
}
@Override
@Transactional
public <S extends T> List<S> saveAll(@Nullable AccessController.Operation operation, final Iterable<S> entities) {
return saveAll(entities);
}
@NonNull
public Optional<T> findOne(@Nullable AccessController.Operation operation, @Nullable Specification<T> spec) {
return findOne(spec);
}
@Override
@NonNull
public List<T> findAll(@Nullable final AccessController.Operation operation, @Nullable final Specification<T> spec) {
return findAll(spec);
}
@Override
@NonNull
public boolean exists(@Nullable AccessController.Operation operation, Specification<T> spec) {
return exists(spec);
}
@Override
@NonNull
public long count(@Nullable final AccessController.Operation operation, @Nullable final Specification<T> spec) {
return count(spec);
}
@NonNull
@Override
public Slice<T> findAllWithoutCount(@Nullable final AccessController.Operation operation, @Nullable Specification<T> spec, Pageable pageable) {
return findAllWithoutCount(spec, pageable);
}
@Override
@NonNull
public Class<T> getDomainClass() {
return super.getDomainClass();
}
@Override
public String toString() {
return getClass().getSimpleName() + '<' + getDomainClass().getSimpleName() + '>';
}
private <S extends T> Page<S> readPageWithoutCount(final TypedQuery<S> query, final Pageable pageable) {
query.setFirstResult((int) pageable.getOffset());
query.setMaxResults(pageable.getPageSize());
final List<S> content = query.getResultList();
return new PageImpl<>(content, pageable, content.size());
}
/**
* Simple implementation of {@link BaseRepositoryTypeProvider} leveraging our
* {@link HawkBitBaseRepository} for all current use cases
*/
public static class RepositoryTypeProvider implements BaseRepositoryTypeProvider {
@Override
public Class<?> getBaseRepositoryType(final Class<?> repositoryType) {
return HawkBitBaseRepository.class;
}
}
}

View File

@@ -7,16 +7,13 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.repository;
import java.util.List;
import java.util.Optional; import java.util.Optional;
import org.eclipse.hawkbit.repository.jpa.model.JpaArtifact; import org.eclipse.hawkbit.repository.jpa.model.JpaArtifact;
import org.eclipse.hawkbit.repository.model.Artifact; import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@@ -26,55 +23,47 @@ import org.springframework.transaction.annotation.Transactional;
* *
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true)
public interface LocalArtifactRepository extends BaseEntityRepository<JpaArtifact, Long> { public interface LocalArtifactRepository
extends BaseEntityRepository<JpaArtifact> {
/** /**
* Counts artifacts size where the related software module is not * Counts artifacts size where the related software module is not
* deleted/archived. * deleted/archived.
* <p/>
* No access control applied.
* *
* @return sum of artifacts size in bytes * @return sum of artifacts size in bytes
*/ */
@Query("SELECT SUM(la.size) FROM JpaArtifact la WHERE la.softwareModule.deleted = 0") @Query("SELECT SUM(la.size) FROM JpaArtifact la WHERE la.softwareModule.deleted = false")
Optional<Long> getSumOfUndeletedArtifactSize(); Optional<Long> sumOfNonDeletedArtifactSize();
/** /**
* Counts artifacts where the related software module is deleted/archived. * Counts artifacts where the related software module is deleted/archived.
* <p/>
* No access control applied
* *
* @param deleted * @param deleted to true for counting the deleted artifacts
* to true for counting the deleted artifacts
*
* @return number of artifacts * @return number of artifacts
*/ */
Long countBySoftwareModuleDeleted(boolean deleted); Long countBySoftwareModuleDeleted(boolean deleted);
/** /**
* Searches for a {@link Artifact} based on given gridFsFileName. * Counts current elements based on the sha1 and tenant, as well as having the
* * {@link SoftwareModule} property 'deleted' with value 'false'
* @param sha1Hash * <p/>
* to search * No access control applied
* @return list of {@link Artifact}s.
*/
List<Artifact> findBySha1Hash(String sha1Hash);
/**
* Counts current elements based on the sha1 and tenant, as well as having
* the {@link SoftwareModule} property 'deleted' with value 'false
*
* @param sha1
* the sha1 of the {@link Artifact}
* @param tenant
* the current tenant
* *
* @param sha1 the sha1 of the {@link Artifact}
* @param tenant the current tenant\
* @return the count of the elements * @return the count of the elements
*/ */
long countBySha1HashAndTenantAndSoftwareModuleDeletedIsFalse(@Param("sha1") String sha1, long countBySha1HashAndTenantAndSoftwareModuleDeletedIsFalse(
@Param("tenant") String tenant); @Param("sha1") String sha1, @Param("tenant") String tenant);
/** /**
* Searches for a {@link Artifact} based on given gridFsFileName. * Searches for a {@link Artifact} based on given gridFsFileName.
* *
* @param sha1Hash * @param sha1Hash to search
* to search
* @return {@link Artifact} the first in the result list * @return {@link Artifact} the first in the result list
*/ */
Optional<Artifact> findFirstBySha1Hash(String sha1Hash); Optional<Artifact> findFirstBySha1Hash(String sha1Hash);
@@ -82,38 +71,14 @@ public interface LocalArtifactRepository extends BaseEntityRepository<JpaArtifac
/** /**
* Searches for a {@link Artifact} based user provided filename at upload. * Searches for a {@link Artifact} based user provided filename at upload.
* *
* @param filename * @param filename to search
* to search
* @return list of {@link Artifact}. * @return list of {@link Artifact}.
*/ */
Optional<Artifact> findFirstByFilename(String filename); Optional<Artifact> findFirstByFilename(String filename);
/** /**
* Searches for local artifact for a base software module. * Searches for a {@link Artifact} based user provided filename at upload and
* * selected software module id.
* @param pageReq
* Pageable
* @param softwareModuleId
* software module id
*
* @return Page<Artifact>
*/
Page<Artifact> findBySoftwareModuleId(Pageable pageReq, Long softwareModuleId);
/**
* Count the artifacts that are associated with the given software module.
*
* @param softwareModuleId
* software module ID
*
* @return the current number of artifacts associated with the software
* module.
*/
long countBySoftwareModuleId(Long softwareModuleId);
/**
* Searches for a {@link Artifact} based user provided filename at upload
* and selected software module id.
* *
* @param filename * @param filename
* to search * to search
@@ -123,5 +88,4 @@ public interface LocalArtifactRepository extends BaseEntityRepository<JpaArtifac
* @return list of {@link Artifact}. * @return list of {@link Artifact}.
*/ */
Optional<Artifact> findFirstByFilenameAndSoftwareModuleId(String filename, Long softwareModuleId); Optional<Artifact> findFirstByFilenameAndSoftwareModuleId(String filename, Long softwareModuleId);
} }

View File

@@ -7,7 +7,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.repository;
import org.eclipse.hawkbit.repository.model.BaseEntity; import org.eclipse.hawkbit.repository.model.BaseEntity;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;

View File

@@ -7,7 +7,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.repository;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@@ -19,7 +19,6 @@ import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupStatus; import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupStatus;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
@@ -30,7 +29,7 @@ import org.springframework.transaction.annotation.Transactional;
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true)
public interface RolloutGroupRepository public interface RolloutGroupRepository
extends BaseEntityRepository<JpaRolloutGroup, Long>, JpaSpecificationExecutor<JpaRolloutGroup> { extends BaseEntityRepository<JpaRolloutGroup> {
/** /**
* Retrieves all {@link RolloutGroup} referring a specific rollout in the * Retrieves all {@link RolloutGroup} referring a specific rollout in the

View File

@@ -7,7 +7,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.repository;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@@ -20,7 +20,6 @@ import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Rollout; import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus; import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity; import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
@@ -31,7 +30,7 @@ import org.springframework.transaction.annotation.Transactional;
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true)
public interface RolloutRepository public interface RolloutRepository
extends BaseEntityRepository<JpaRollout, Long>, JpaSpecificationExecutor<JpaRollout> { extends BaseEntityRepository<JpaRollout> {
/** /**
* Retrieves all {@link Rollout} for given status. * Retrieves all {@link Rollout} for given status.

View File

@@ -7,7 +7,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.repository;
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup; import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup;
import org.eclipse.hawkbit.repository.jpa.model.RolloutTargetGroup; import org.eclipse.hawkbit.repository.jpa.model.RolloutTargetGroup;

View File

@@ -7,7 +7,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.repository;
import java.util.Collection; import java.util.Collection;
@@ -52,14 +52,12 @@ public interface SoftwareModuleMetadataRepository
/** /**
* Locates the meta data entries that match the given software module IDs * Locates the meta data entries that match the given software module IDs
* and target visibility flag. * and target visibility flag.
* <p/>
* No access control applied
* *
* @param page * @param page The pagination parameters.
* The pagination parameters. * @param moduleId List of software module IDs.
* @param moduleId * @param targetVisible The target visibility flag.
* List of software module IDs.
* @param targetVisible
* The target visibility flag.
*
* @return A {@link Page} with the matching meta data entries. * @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") @Query("SELECT smd.softwareModule.id, smd FROM JpaSoftwareModuleMetadata smd WHERE smd.softwareModule.id IN :moduleId AND smd.targetVisible = :targetVisible")
@@ -69,13 +67,11 @@ public interface SoftwareModuleMetadataRepository
/** /**
* Counts the meta data entries that are associated with the addressed * Counts the meta data entries that are associated with the addressed
* software module. * software module.
* <p/>
* No access control applied
* *
* @param moduleId * @param moduleId The ID of the software module.
* 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); long countBySoftwareModuleId(@Param("moduleId") Long moduleId);
} }

View File

@@ -0,0 +1,66 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.repository;
import javax.persistence.EntityManager;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;
/**
* {@link SoftwareModule} repository.
*
*/
@Transactional(readOnly = true)
public interface SoftwareModuleRepository
extends BaseEntityRepository<JpaSoftwareModule> {
/**
* Counts all {@link SoftwareModule}s based on the given {@link JpaSoftwareModuleType}.
* <p/>
* No access control applied
*
* @param type to count for
* @return number of {@link SoftwareModule}s
*/
long countByType(JpaSoftwareModuleType type);
/**
* Count the software modules which are assigned to the distribution set
* with the given ID.
* <p/>
* No access control applied
*
* @param distributionSetId the distribution set ID
*
* @return the number of software modules matching the given distribution set ID.
*/
long countByAssignedToId(Long distributionSetId);
/**
* Deletes all {@link TenantAwareBaseEntity} of a given tenant. For safety
* reasons (this is a "delete everything" query after all) we add the tenant
* manually to query even if this will by done by {@link EntityManager}
* anyhow. The DB should take care of optimizing this away.
*
* @param tenant
* to delete data from
*/
@Modifying
@Transactional
@Query("DELETE FROM JpaSoftwareModule t WHERE t.tenant = :tenant")
void deleteByTenant(@Param("tenant") String tenant);
}

View File

@@ -7,9 +7,8 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.repository;
import java.util.List;
import java.util.Optional; import java.util.Optional;
import javax.persistence.EntityManager; import javax.persistence.EntityManager;
@@ -17,9 +16,6 @@ import javax.persistence.EntityManager;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType; import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity; import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
@@ -31,24 +27,7 @@ import org.springframework.transaction.annotation.Transactional;
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true)
public interface SoftwareModuleTypeRepository public interface SoftwareModuleTypeRepository
extends BaseEntityRepository<JpaSoftwareModuleType, Long>, JpaSpecificationExecutor<JpaSoftwareModuleType> { extends BaseEntityRepository<JpaSoftwareModuleType> {
/**
* @param pageable
* @param isDeleted
* to <code>true</code> if only marked as deleted have to be
* count or all undeleted.
* @return found {@link SoftwareModuleType}s.
*/
Page<SoftwareModuleType> findByDeleted(Pageable pageable, boolean isDeleted);
/**
* @param isDeleted
* to <code>true</code> if only marked as deleted have to be
* count or all undeleted.
* @return number of {@link SoftwareModuleType}s in the repository.
*/
Long countByDeleted(boolean isDeleted);
/** /**
* *
@@ -81,9 +60,4 @@ public interface SoftwareModuleTypeRepository
@Transactional @Transactional
@Query("DELETE FROM JpaSoftwareModuleType t WHERE t.tenant = :tenant") @Query("DELETE FROM JpaSoftwareModuleType t WHERE t.tenant = :tenant")
void deleteByTenant(@Param("tenant") String tenant); void deleteByTenant(@Param("tenant") String tenant);
@Override
// Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477
@Query("SELECT d FROM JpaSoftwareModuleType d WHERE d.id IN ?1")
List<JpaSoftwareModuleType> findAllById(Iterable<Long> ids);
} }

View File

@@ -7,7 +7,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.repository;
import java.util.Optional; import java.util.Optional;
@@ -17,7 +17,6 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery; import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity; import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
@@ -29,7 +28,7 @@ import org.springframework.transaction.annotation.Transactional;
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true)
public interface TargetFilterQueryRepository public interface TargetFilterQueryRepository
extends BaseEntityRepository<JpaTargetFilterQuery, Long>, JpaSpecificationExecutor<JpaTargetFilterQuery> { extends BaseEntityRepository<JpaTargetFilterQuery> {
/** /**
* Find customer target filter by name * Find customer target filter by name
@@ -39,27 +38,24 @@ public interface TargetFilterQueryRepository
*/ */
Optional<TargetFilterQuery> findByName(String name); Optional<TargetFilterQuery> findByName(String name);
/**
* Find list of all custom target filters.
*/
@Override
Page<JpaTargetFilterQuery> findAll();
/** /**
* Sets the auto assign distribution sets and action types to null which * Sets the auto assign distribution sets and action types to null which
* match the ds ids. * match the ds ids.
* <p/>
* No access control applied
* *
* @param dsIds * @param dsIds distribution set ids to be set to null
* distribution set ids to be set to null
*/ */
@Modifying @Modifying
@Transactional @Transactional
@Query("update JpaTargetFilterQuery d set d.autoAssignDistributionSet = NULL, d.autoAssignActionType = NULL where d.autoAssignDistributionSet in :ids") @Query("update JpaTargetFilterQuery d set d.autoAssignDistributionSet = NULL, d.autoAssignActionType = NULL, d.accessControlContext = NULL where d.autoAssignDistributionSet in :ids")
void unsetAutoAssignDistributionSetAndActionType(@Param("ids") Long... dsIds); void unsetAutoAssignDistributionSetAndActionTypeAndAccessContext(@Param("ids") Long... dsIds);
/** /**
* Counts all target filters that have a given auto assign distribution set * Counts all target filters that have a given auto assign distribution set
* assigned. * assigned.
* <p/>
* No access control applied
* *
* @param autoAssignDistributionSetId * @param autoAssignDistributionSetId
* the id of the distribution set * the id of the distribution set
@@ -80,5 +76,4 @@ public interface TargetFilterQueryRepository
@Transactional @Transactional
@Query("DELETE FROM JpaTargetFilterQuery t WHERE t.tenant = :tenant") @Query("DELETE FROM JpaTargetFilterQuery t WHERE t.tenant = :tenant")
void deleteByTenant(@Param("tenant") String tenant); void deleteByTenant(@Param("tenant") String tenant);
} }

View File

@@ -7,7 +7,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.repository;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetMetadata; import org.eclipse.hawkbit.repository.jpa.model.JpaTargetMetadata;
import org.eclipse.hawkbit.repository.jpa.model.TargetMetadataCompositeKey; import org.eclipse.hawkbit.repository.jpa.model.TargetMetadataCompositeKey;
@@ -29,10 +29,10 @@ public interface TargetMetadataRepository
/** /**
* Counts the meta data entries that match the given target ID. * Counts the meta data entries that match the given target ID.
* <p/>
* No access control applied
* *
* @param id * @param id of the target.
* of the target.
*
* @return The number of matching meta data entries. * @return The number of matching meta data entries.
*/ */
long countByTargetId(@Param("id") Long id); long countByTargetId(@Param("id") Long id);

View File

@@ -0,0 +1,83 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.repository;
import java.util.Collection;
import javax.persistence.EntityManager;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;
/**
* {@link Target} repository.
*
*/
@Transactional(readOnly = true)
public interface TargetRepository extends BaseEntityRepository<JpaTarget> {
// TODO AC - remove it and use specification
/**
* @deprecated remove it and use specification
*/
// no access check
@Deprecated(forRemoval = true)
@Modifying
@Transactional
@Query("UPDATE JpaTarget t SET t.assignedDistributionSet = :set, t.lastModifiedAt = :lastModifiedAt, t.lastModifiedBy = :lastModifiedBy, t.updateStatus = :status WHERE t.id IN :targets")
void setAssignedDistributionSetAndUpdateStatus(@Param("status") TargetUpdateStatus status,
@Param("set") JpaDistributionSet set, @Param("lastModifiedAt") Long modifiedAt,
@Param("lastModifiedBy") String modifiedBy, @Param("targets") Collection<Long> targets);
// TODO AC - remove it and use specification
/**
* @deprecated will be removed
*/
// no access check
@Deprecated(forRemoval = true)
@Modifying
@Transactional
@Query("UPDATE JpaTarget t SET t.assignedDistributionSet = :set, t.installedDistributionSet = :set, t.installationDate = :lastModifiedAt, t.lastModifiedAt = :lastModifiedAt, t.lastModifiedBy = :lastModifiedBy, t.updateStatus = :status WHERE t.id IN :targets")
void setAssignedAndInstalledDistributionSetAndUpdateStatus(@Param("status") TargetUpdateStatus status,
@Param("set") JpaDistributionSet set, @Param("lastModifiedAt") Long modifiedAt,
@Param("lastModifiedBy") String modifiedBy, @Param("targets") Collection<Long> targets);
/**
* Counts {@link Target} instances of given type in the repository.
* <p/>
* No access control applied
*
* @param targetTypeId to search for
* @return number of found {@link Target}s
*/
long countByTargetTypeId(Long targetTypeId);
/**
* Deletes all {@link TenantAwareBaseEntity} of a given tenant. For safety
* reasons (this is a "delete everything" query after all) we add the tenant
* manually to query even if this will by done by {@link EntityManager} anyhow.
* The DB should take care of optimizing this away.
*
* @param tenant to delete data from
*/
@Modifying
@Transactional
@Query("DELETE FROM JpaTarget t WHERE t.tenant = :tenant")
void deleteByTenant(@Param("tenant") String tenant);
}

View File

@@ -7,9 +7,8 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.repository;
import java.util.List;
import java.util.Optional; import java.util.Optional;
import javax.persistence.EntityManager; import javax.persistence.EntityManager;
@@ -17,7 +16,6 @@ import javax.persistence.EntityManager;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag; import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
import org.eclipse.hawkbit.repository.model.TargetTag; import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity; import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
@@ -28,19 +26,7 @@ import org.springframework.transaction.annotation.Transactional;
* *
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true)
public interface TargetTagRepository public interface TargetTagRepository extends BaseEntityRepository<JpaTargetTag> {
extends BaseEntityRepository<JpaTargetTag, Long>, JpaSpecificationExecutor<JpaTargetTag> {
/**
* deletes the {@link TargetTag}s with the given tag names.
*
* @param tagName
* to be deleted
* @return 1 if tag was deleted
*/
@Modifying
@Transactional
Long deleteByName(String tagName);
/** /**
* find {@link TargetTag} by its name. * find {@link TargetTag} by its name.
@@ -51,24 +37,6 @@ public interface TargetTagRepository
*/ */
Optional<TargetTag> findByNameEquals(String tagName); Optional<TargetTag> findByNameEquals(String tagName);
/**
* Checks if tag with given name exists.
*
* @param tagName
* to check for
* @return <code>true</code> is tag with given name exists
*/
@Query("SELECT CASE WHEN COUNT(t)>0 THEN 'true' ELSE 'false' END FROM JpaTargetTag t WHERE t.name=:tagName")
boolean existsByName(@Param("tagName") String tagName);
/**
* Returns all instances of the type.
*
* @return all entities
*/
@Override
List<JpaTargetTag> findAll();
/** /**
* Deletes all {@link TenantAwareBaseEntity} of a given tenant. For safety * Deletes all {@link TenantAwareBaseEntity} of a given tenant. For safety
* reasons (this is a "delete everything" query after all) we add the tenant * reasons (this is a "delete everything" query after all) we add the tenant
@@ -82,9 +50,4 @@ public interface TargetTagRepository
@Transactional @Transactional
@Query("DELETE FROM JpaTargetTag t WHERE t.tenant = :tenant") @Query("DELETE FROM JpaTargetTag t WHERE t.tenant = :tenant")
void deleteByTenant(@Param("tenant") String tenant); void deleteByTenant(@Param("tenant") String tenant);
@Override
// Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477
@Query("SELECT t FROM JpaTargetTag t WHERE t.id IN ?1")
List<JpaTargetTag> findAllById(Iterable<Long> ids);
} }

View File

@@ -0,0 +1,74 @@
/**
* Copyright (c) 2021 Bosch.IO GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.repository;
import java.util.List;
import java.util.Optional;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetTypeSpecification;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;
/**
* {@link PagingAndSortingRepository} and {@link org.springframework.data.repository.CrudRepository} for
* {@link JpaTargetType}.
*
*/
@Transactional(readOnly = true)
public interface TargetTypeRepository
extends BaseEntityRepository<JpaTargetType> {
/**
* Counts the distributions set types compatible with that type
* <p/>
* No access control applied.
*
* @param id target type id
* @return the count
*/
@Query(value = "SELECT COUNT (t.id) FROM JpaDistributionSetType t JOIN t.compatibleToTargetTypes tt WHERE tt.id = :id")
long countDsSetTypesById(@Param("id") Long id);
/**
*
* @param dsTypeId
* to search for
* @return all {@link TargetType}s in the repository with given
* {@link TargetType#getName()}
*/
default List<JpaTargetType> findByDsType(@Param("id") final Long dsTypeId) {
return this.findAll(Specification.where(TargetTypeSpecification.hasDsSetType(dsTypeId)));
}
/**
*
* @param name
* to search for
* @return all {@link TargetType}s in the repository with given
* {@link TargetType#getName()}
*/
default Optional<JpaTargetType> findByName(final String name) {
return this.findOne(Specification.where(TargetTypeSpecification.hasName(name)));
}
/**
* @param tenant Tenant
*/
@Modifying
@Transactional
@Query("DELETE FROM JpaTargetType t WHERE t.tenant = :tenant")
void deleteByTenant(@Param("tenant") String tenant);
}

View File

@@ -7,7 +7,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.repository;
import javax.persistence.EntityManager; import javax.persistence.EntityManager;
@@ -24,7 +24,7 @@ import org.springframework.transaction.annotation.Transactional;
* *
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true)
public interface TenantConfigurationRepository extends BaseEntityRepository<JpaTenantConfiguration, Long> { public interface TenantConfigurationRepository extends BaseEntityRepository<JpaTenantConfiguration> {
/** /**
* Finds a specific {@link TenantConfiguration} by the configuration key. * Finds a specific {@link TenantConfiguration} by the configuration key.

View File

@@ -7,7 +7,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa.repository;
import java.util.List; import java.util.List;

View File

@@ -10,7 +10,7 @@
package org.eclipse.hawkbit.repository.jpa.rollout.condition; package org.eclipse.hawkbit.repository.jpa.rollout.condition;
import org.eclipse.hawkbit.repository.RolloutManagement; import org.eclipse.hawkbit.repository.RolloutManagement;
import org.eclipse.hawkbit.repository.jpa.RolloutGroupRepository; import org.eclipse.hawkbit.repository.jpa.repository.RolloutGroupRepository;
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup; import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup;
import org.eclipse.hawkbit.repository.model.Rollout; import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.RolloutGroup; import org.eclipse.hawkbit.repository.model.RolloutGroup;

View File

@@ -12,7 +12,7 @@ package org.eclipse.hawkbit.repository.jpa.rollout.condition;
import java.util.List; import java.util.List;
import org.eclipse.hawkbit.repository.DeploymentManagement; import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.jpa.RolloutGroupRepository; import org.eclipse.hawkbit.repository.jpa.repository.RolloutGroupRepository;
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup; import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup;
import org.eclipse.hawkbit.repository.model.Rollout; import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.RolloutGroup; import org.eclipse.hawkbit.repository.model.RolloutGroup;

View File

@@ -9,7 +9,7 @@
*/ */
package org.eclipse.hawkbit.repository.jpa.rollout.condition; package org.eclipse.hawkbit.repository.jpa.rollout.condition;
import org.eclipse.hawkbit.repository.jpa.ActionRepository; import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout; import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup; import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;

View File

@@ -9,7 +9,7 @@
*/ */
package org.eclipse.hawkbit.repository.jpa.rollout.condition; package org.eclipse.hawkbit.repository.jpa.rollout.condition;
import org.eclipse.hawkbit.repository.jpa.ActionRepository; import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Rollout; import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.RolloutGroup; import org.eclipse.hawkbit.repository.model.RolloutGroup;

View File

@@ -9,7 +9,9 @@
*/ */
package org.eclipse.hawkbit.repository.jpa.specifications; package org.eclipse.hawkbit.repository.jpa.specifications;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Join; import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.ListJoin; import javax.persistence.criteria.ListJoin;
import javax.persistence.criteria.SetJoin; import javax.persistence.criteria.SetJoin;
@@ -25,6 +27,8 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.domain.Specification;
import java.util.List;
/** /**
* Utility class for {@link Action}s {@link Specification}s. The class provides * Utility class for {@link Action}s {@link Specification}s. The class provides
* Spring Data JPQL Specifications. * Spring Data JPQL Specifications.
@@ -36,6 +40,81 @@ public final class ActionSpecifications {
// utility class // utility class
} }
public static Specification<JpaAction> byTargetIdAndIsActive(final Long targetId) {
return (root, query, cb) -> cb.and(
cb.equal(root.get(JpaAction_.target).get(JpaTarget_.id), targetId),
cb.equal(root.get(JpaAction_.active), true));
}
public static Specification<JpaAction> byTargetControllerId(final String controllerId) {
return (root, query, cb) -> cb.equal(root.get(JpaAction_.target).get(JpaTarget_.controllerId), controllerId);
}
public static Specification<JpaAction> byTargetIdAndIsActiveAndStatus(final Long targetId, final Action.Status status) {
return (root, query, cb) -> cb.and(
cb.equal(root.get(JpaAction_.target).get(JpaTarget_.id), targetId),
cb.equal(root.get(JpaAction_.active), true),
cb.equal(root.get(JpaAction_.status), status));
}
public static Specification<JpaAction> byTargetControllerIdAndActive(final String controllerId, final boolean active) {
return (root, query, cb) -> cb.and(
cb.equal(root.get(JpaAction_.target).get(JpaTarget_.controllerId), controllerId),
cb.equal(root.get(JpaAction_.active), active));
}
public static Specification<JpaAction> byTargetControllerIdAndIsActiveAndStatus(final String controllerId, final Action.Status status) {
return (root, query, cb) -> cb.and(
cb.equal(root.get(JpaAction_.target).get(JpaTarget_.controllerId), controllerId),
cb.equal(root.get(JpaAction_.active), true),
cb.equal(root.get(JpaAction_.status), status));
}
public static Specification<JpaAction> byTargetIdsAndActiveAndStatusAndDSNotRequiredMigrationStep(
final List<Long> targetIds, final boolean active, final Action.Status status) {
return (root, query, cb) -> cb.and(
root.get(JpaAction_.target).in(targetIds),
cb.equal(root.get(JpaAction_.active), active),
cb.equal(root.get(JpaAction_.status), status),
cb.equal(root.get(JpaAction_.distributionSet).get(JpaDistributionSet_.requiredMigrationStep), false));
}
/**
* Returns active actions by target controller that has null or non-null depending on <code>isNull</code> value.
* 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(
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)));
};
}
public static Specification<JpaAction> byDistributionSetId(final Long distributionSetId) {
return (root, query, cb) -> cb.equal(root.get(JpaAction_.distributionSet).get(JpaTarget_.id), distributionSetId);
}
public static Specification<JpaAction> byDistributionSetIdAndActive(final Long distributionSetId) {
return (root, query, cb) -> cb.and(
cb.equal(root.get(JpaAction_.distributionSet).get(JpaTarget_.id), distributionSetId),
cb.equal(root.get(JpaAction_.active), true));
}
public static Specification<JpaAction> byDistributionSetIdAndActiveAndStatusIsNot(
final Long distributionSetId, final Action.Status status) {
return (root, query, cb) -> cb.and(
cb.equal(root.get(JpaAction_.distributionSet).get(JpaTarget_.id), distributionSetId),
cb.equal(root.get(JpaAction_.active), true),
cb.notEqual(root.get(JpaAction_.status), status));
}
/** /**
* Specification which joins all necessary tables to retrieve the dependency * Specification which joins all necessary tables to retrieve the dependency
* between a target and a local file assignment through the assigned action * between a target and a local file assignment through the assigned action

View File

@@ -0,0 +1,31 @@
/**
* Copyright (c) 2023 Bosch.IO GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.specifications;
import org.eclipse.hawkbit.repository.jpa.model.JpaArtifact;
import org.eclipse.hawkbit.repository.jpa.model.JpaArtifact_;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule_;
import org.springframework.data.jpa.domain.Specification;
/**
* Utility class for {@link JpaArtifact}s {@link Specification}s. The class provides
* Spring Data JPQL Specifications.
*/
public final class ArtifactSpecifications {
private ArtifactSpecifications() {
// utility class
}
public static Specification<JpaArtifact> bySoftwareModuleId(final Long softwareModuleId) {
return (targetRoot, query, cb) -> cb.equal(
targetRoot.get(JpaArtifact_.softwareModule).get(JpaSoftwareModule_.id), softwareModuleId);
}
}

View File

@@ -23,6 +23,8 @@ import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root; import javax.persistence.criteria.Root;
import javax.persistence.criteria.SetJoin; import javax.persistence.criteria.SetJoin;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction_;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag_; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag_;
@@ -46,6 +48,16 @@ public final class DistributionSetSpecification {
// utility class // utility class
} }
/**
* {@link Specification} for retrieving {@link DistributionSet}s with
* DELETED attribute <code>false</code> - i.e. is not deleted.
*
* @return the {@link DistributionSet} {@link Specification}
*/
public static Specification<JpaDistributionSet> isNotDeleted() {
return isDeleted(false);
}
/** /**
* {@link Specification} for retrieving {@link DistributionSet}s by its * {@link Specification} for retrieving {@link DistributionSet}s by its
* DELETED attribute. * DELETED attribute.
@@ -57,26 +69,25 @@ public final class DistributionSetSpecification {
*/ */
public static Specification<JpaDistributionSet> isDeleted(final Boolean isDeleted) { public static Specification<JpaDistributionSet> isDeleted(final Boolean isDeleted) {
return (dsRoot, query, cb) -> cb.equal(dsRoot.<Boolean> get(JpaDistributionSet_.deleted), isDeleted); return (dsRoot, query, cb) -> cb.equal(dsRoot.<Boolean> get(JpaDistributionSet_.deleted), isDeleted);
} }
/** /**
* {@link Specification} for retrieving {@link DistributionSet}s by its * {@link Specification} for retrieving {@link DistributionSet}s by its
* COMPLETED attribute. * COMPLETED attribute.
* *
* @param isCompleted * @param isCompleted
* TRUE/FALSE are compared to the attribute COMPLETED. If NULL * TRUE/FALSE are compared to the attribute COMPLETED. If NULL the
* the attribute is ignored * attribute is ignored
* @return the {@link DistributionSet} {@link Specification} * @return the {@link DistributionSet} {@link Specification}
*/ */
public static Specification<JpaDistributionSet> isCompleted(final Boolean isCompleted) { public static Specification<JpaDistributionSet> isCompleted(final Boolean isCompleted) {
return (dsRoot, query, cb) -> cb.equal(dsRoot.<Boolean> get(JpaDistributionSet_.complete), isCompleted); return (dsRoot, query, cb) -> cb.equal(dsRoot.<Boolean> get(JpaDistributionSet_.complete), isCompleted);
} }
/** /**
* {@link Specification} for retrieving {@link DistributionSet}s by its * {@link Specification} for retrieving {@link DistributionSet}s by its VALID
* VALID attribute. * attribute.
* *
* @param isValid * @param isValid
* TRUE/FALSE are compared to the attribute VALID. If NULL the * TRUE/FALSE are compared to the attribute VALID. If NULL the
@@ -85,7 +96,6 @@ public final class DistributionSetSpecification {
*/ */
public static Specification<JpaDistributionSet> isValid(final Boolean isValid) { public static Specification<JpaDistributionSet> isValid(final Boolean isValid) {
return (dsRoot, query, cb) -> cb.equal(dsRoot.<Boolean> get(JpaDistributionSet_.valid), isValid); return (dsRoot, query, cb) -> cb.equal(dsRoot.<Boolean> get(JpaDistributionSet_.valid), isValid);
} }
/** /**
@@ -107,6 +117,15 @@ public final class DistributionSetSpecification {
}; };
} }
public static Specification<JpaDistributionSet> byActionId(final Long actionId) {
return (dsRoot, query, cb) -> {
final ListJoin<JpaDistributionSet, JpaAction> join = dsRoot.join(JpaDistributionSet_.actions,
JoinType.LEFT);
query.distinct(true);
return cb.equal(join.get(JpaAction_.id), actionId);
};
}
/** /**
* {@link Specification} for retrieving {@link DistributionSet} with given * {@link Specification} for retrieving {@link DistributionSet} with given
* {@link DistributionSet#getId()}s. * {@link DistributionSet#getId()}s.
@@ -127,8 +146,8 @@ public final class DistributionSetSpecification {
} }
/** /**
* {@link Specification} for retrieving {@link DistributionSet}s by "like * {@link Specification} for retrieving {@link DistributionSet}s by "like name
* name and like version". * and like version".
* *
* @param name * @param name
* to be filtered on * to be filtered on
@@ -188,8 +207,8 @@ public final class DistributionSetSpecification {
} }
/** /**
* returns query criteria {@link Specification} comparing case insensitive * returns query criteria {@link Specification} comparing case insensitive "NAME
* "NAME == AND VERSION ==". * == AND VERSION ==".
* *
* @param name * @param name
* to be filtered on * to be filtered on
@@ -216,15 +235,27 @@ public final class DistributionSetSpecification {
public static Specification<JpaDistributionSet> byType(final Long typeId) { public static Specification<JpaDistributionSet> byType(final Long typeId) {
return (dsRoot, query, cb) -> cb.equal(dsRoot.get(JpaDistributionSet_.type).get(JpaDistributionSetType_.id), return (dsRoot, query, cb) -> cb.equal(dsRoot.get(JpaDistributionSet_.type).get(JpaDistributionSetType_.id),
typeId); typeId);
}
/**
* {@link Specification} for retrieving {@link DistributionSet} for given id
* collection of {@link DistributionSet#getType()}.
*
*
* @param typeIds
* id collection of distribution set type to search
* @return the {@link DistributionSet} {@link Specification}
*/
public static Specification<JpaDistributionSet> hasType(final Collection<Long> typeIds) {
return (dsRoot, query, cb) -> dsRoot.get(JpaDistributionSet_.type).get(JpaDistributionSetType_.id).in(typeIds);
} }
/** /**
* @param installedTargetId * @param installedTargetId
* the targetID which is installed to a distribution set to * the targetID which is installed to a distribution set to search
* search for. * for.
* @return the specification to search for a distribution set which is * @return the specification to search for a distribution set which is installed
* installed to the given targetId * to the given targetId
*/ */
public static Specification<JpaDistributionSet> installedTarget(final String installedTargetId) { public static Specification<JpaDistributionSet> installedTarget(final String installedTargetId) {
return (dsRoot, query, cb) -> { return (dsRoot, query, cb) -> {
@@ -238,8 +269,8 @@ public final class DistributionSetSpecification {
* @param assignedTargetId * @param assignedTargetId
* the targetID which is assigned to a distribution set to search * the targetID which is assigned to a distribution set to search
* for. * for.
* @return the specification to search for a distribution set which is * @return the specification to search for a distribution set which is assigned
* assigned to the given targetId * to the given targetId
*/ */
public static Specification<JpaDistributionSet> assignedTarget(final String assignedTargetId) { public static Specification<JpaDistributionSet> assignedTarget(final String assignedTargetId) {
return (dsRoot, query, cb) -> { return (dsRoot, query, cb) -> {
@@ -269,12 +300,11 @@ public final class DistributionSetSpecification {
* Can be added to specification chain to order result by provided target * Can be added to specification chain to order result by provided target
* *
* Order: 1. Distribution set installed on target, 2. Distribution set(s) * Order: 1. Distribution set installed on target, 2. Distribution set(s)
* assigned to target, 3. Based on requested sorting or id if * assigned to target, 3. Based on requested sorting or id if <code>null</code>.
* <code>null</code>.
* *
* NOTE: Other specs, pagables and sort objects may alter the queries * NOTE: Other specs, pagables and sort objects may alter the queries orderBy
* orderBy entry too, possibly invalidating the applied order, keep in mind * entry too, possibly invalidating the applied order, keep in mind when using
* when using this * this
* *
* @param linkedControllerId * @param linkedControllerId
* controller id to get installed/assigned DS for * controller id to get installed/assigned DS for

View File

@@ -0,0 +1,31 @@
/**
* Copyright (c) 2023 Bosch.IO GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.specifications;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag_;
import org.springframework.data.jpa.domain.Specification;
import javax.validation.constraints.NotEmpty;
/**
* Utility class for {@link JpaDistributionSetTag}s {@link Specification}s. The class provides
* Spring Data JPQL Specifications.
*/
public final class DistributionSetTagSpecifications {
private DistributionSetTagSpecifications() {
// utility class
}
public static Specification<JpaDistributionSetTag> byName(@NotEmpty final String name) {
return (targetRoot, query, cb) -> cb.equal(targetRoot.get(JpaDistributionSetTag_.name), name);
}
}

View File

@@ -25,30 +25,13 @@ public final class DistributionSetTypeSpecification {
} }
/** /**
* {@link Specification} for retrieving {@link DistributionSetType}s by its * {@link Specification} for retrieving {@link DistributionSetType}s with
* DELETED attribute. * DELETED attribute <code>false</code> - i.e. is not deleted.
* *
* @param isDeleted
* TRUE/FALSE are compared to the attribute DELETED. If NULL the
* attribute is ignored
* @return the {@link DistributionSetType} {@link Specification} * @return the {@link DistributionSetType} {@link Specification}
*/ */
public static Specification<JpaDistributionSetType> isDeleted(final Boolean isDeleted) { public static Specification<JpaDistributionSetType> isNotDeleted() {
return (targetRoot, query, cb) -> cb.equal(targetRoot.<Boolean> get(JpaDistributionSetType_.deleted), return (targetRoot, query, cb) -> cb.equal(targetRoot.<Boolean> get(JpaDistributionSetType_.deleted), false);
isDeleted);
}
/**
* {@link Specification} for retrieving {@link DistributionSetType} with
* given {@link DistributionSetType#getId()} including fetching the elements
* list.
*
* @param distid
* to search
* @return the {@link DistributionSet} {@link Specification}
*/
public static Specification<JpaDistributionSetType> byId(final Long distid) {
return (targetRoot, query, cb) -> cb.equal(targetRoot.<Long> get(JpaDistributionSetType_.id), distid);
} }
/** /**
@@ -61,7 +44,7 @@ public final class DistributionSetTypeSpecification {
* @return the {@link DistributionSet} {@link Specification} * @return the {@link DistributionSet} {@link Specification}
*/ */
public static Specification<JpaDistributionSetType> byName(final String name) { public static Specification<JpaDistributionSetType> byName(final String name) {
return (targetRoot, query, cb) -> cb.equal(targetRoot.<String> get(JpaDistributionSetType_.name), name); return (targetRoot, query, cb) -> cb.equal(targetRoot.get(JpaDistributionSetType_.name), name);
} }
/** /**
@@ -74,7 +57,6 @@ public final class DistributionSetTypeSpecification {
* @return the {@link DistributionSet} {@link Specification} * @return the {@link DistributionSet} {@link Specification}
*/ */
public static Specification<JpaDistributionSetType> byKey(final String key) { public static Specification<JpaDistributionSetType> byKey(final String key) {
return (targetRoot, query, cb) -> cb.equal(targetRoot.<String> get(JpaDistributionSetType_.key), key); return (targetRoot, query, cb) -> cb.equal(targetRoot.get(JpaDistributionSetType_.key), key);
} }
} }

Some files were not shown because too many files have changed in this diff Show More