[#1651] Add SoftwareModule and DistributionSet unlock (REST) (#1677)

Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2024-03-08 11:28:24 +02:00
committed by GitHub
parent 4d104873de
commit ce9918ce00
10 changed files with 57 additions and 65 deletions

View File

@@ -10,12 +10,20 @@
package org.eclipse.hawkbit.repository.builder;
import jakarta.annotation.Nullable;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.experimental.Accessors;
import java.util.Optional;
/**
* Update implementation.
*/
@Data
@EqualsAndHashCode(callSuper = true)
@ToString
@Accessors(fluent = true)
public class GenericDistributionSetUpdate extends AbstractDistributionSetUpdateCreate<DistributionSetUpdate>
implements DistributionSetUpdate {
@@ -25,17 +33,4 @@ public class GenericDistributionSetUpdate extends AbstractDistributionSetUpdateC
public GenericDistributionSetUpdate(final Long id) {
super.id = id;
}
public DistributionSetUpdate locked(@Nullable final Boolean locked) {
if (Boolean.FALSE.equals(locked)) {
this.locked = null;
} else {
this.locked = locked;
}
return this;
}
public Boolean getLocked() {
return locked;
}
}

View File

@@ -10,12 +10,20 @@
package org.eclipse.hawkbit.repository.builder;
import jakarta.annotation.Nullable;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.experimental.Accessors;
import java.util.Optional;
/**
* Update implementation.
*/
@Data
@EqualsAndHashCode(callSuper = true)
@ToString
@Accessors(fluent = true)
public class GenericSoftwareModuleUpdate extends AbstractSoftwareModuleUpdateCreate<SoftwareModuleUpdate>
implements SoftwareModuleUpdate {
@@ -25,17 +33,4 @@ public class GenericSoftwareModuleUpdate extends AbstractSoftwareModuleUpdateCre
public GenericSoftwareModuleUpdate(final Long id) {
super.id = id;
}
public SoftwareModuleUpdate locked(@Nullable final Boolean locked) {
if (Boolean.FALSE.equals(locked)) {
this.locked = null;
} else {
this.locked = locked;
}
return this;
}
public Boolean getLocked() {
return locked;
}
}

View File

@@ -286,8 +286,11 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
update.getDescription().ifPresent(set::setDescription);
update.getVersion().ifPresent(set::setVersion);
if (Boolean.TRUE.equals(update.getLocked()) && !set.isLocked()) {
// lock/unlock ONLY if locked flag is present!
if (Boolean.TRUE.equals(update.locked())) {
set.lock();
} else if (Boolean.FALSE.equals(update.locked())) {
set.unlock();
}
if (update.isRequiredMigrationStep() != null

View File

@@ -142,8 +142,12 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
update.getDescription().ifPresent(module::setDescription);
update.getVendor().ifPresent(module::setVendor);
if (Boolean.TRUE.equals(update.getLocked()) && !module.isLocked()) {
// lock/unlock ONLY if locked flag is present!
if (Boolean.TRUE.equals(update.locked())) {
module.lock();
} else if (Boolean.FALSE.equals(update.locked())) {
module.unlock();
}
return softwareModuleRepository.save(module);

View File

@@ -39,8 +39,11 @@ public class MgmtDistributionSetRequestBodyPut {
private String version;
@JsonProperty
@Schema(description = "Put it to true only if want to lock the distribution set. Otherwise skip it. " +
"Shall not be false!",
@Schema(description = """
Should be set only if change of locked state is requested. If put, the distribution set locked flag will be
set to the requested. Note: unlock (i.e. set this property to false) with extreme care!
In general once distribution set is locked it shall not be unlocked. Note that it could have been assigned /
deployed to targets.""",
example = "true")
private Boolean locked;

View File

@@ -30,8 +30,11 @@ public class MgmtSoftwareModuleRequestBodyPut {
private String vendor;
@JsonProperty
@Schema(description = "Put it to true only if want to lock the software module. Otherwise skip it. " +
"Shall not be false!",
@Schema(description = """
Should be set only if change of locked state is requested. If put, the software module locked flag will be
set to the requested. Note: unlock (i.e. set this property to false) with extreme care!
In general once software module is locked it shall not be unlocked. Note that it could have been assigned /
deployed to targets.""",
example = "true")
private Boolean locked;
}

View File

@@ -46,44 +46,35 @@ public final class MgmtDistributionSetMapper {
/**
* {@link MgmtDistributionSetRequestBodyPost}s to {@link DistributionSet}s.
*
* @param sets
* to convert
* @param sets to convert
* @return converted list of {@link DistributionSet}s
*/
static List<DistributionSetCreate> dsFromRequest(final Collection<MgmtDistributionSetRequestBodyPost> sets,
final EntityFactory entityFactory) {
return sets.stream().map(dsRest -> fromRequest(dsRest, entityFactory)).collect(Collectors.toList());
}
/**
* {@link MgmtDistributionSetRequestBodyPost} to {@link DistributionSet}.
*
* @param dsRest
* to convert
* @param dsRest to convert
* @return converted {@link DistributionSet}
*/
private static DistributionSetCreate fromRequest(final MgmtDistributionSetRequestBodyPost dsRest,
final EntityFactory entityFactory) {
final List<Long> modules = new ArrayList<>();
if (dsRest.getOs() != null) {
modules.add(dsRest.getOs().getId());
}
if (dsRest.getApplication() != null) {
modules.add(dsRest.getApplication().getId());
}
if (dsRest.getRuntime() != null) {
modules.add(dsRest.getRuntime().getId());
}
if (dsRest.getModules() != null) {
dsRest.getModules().forEach(module -> modules.add(module.getId()));
}
return entityFactory.distributionSet().create().name(dsRest.getName()).version(dsRest.getVersion())
.description(dsRest.getDescription()).type(dsRest.getType()).modules(modules)
.requiredMigrationStep(dsRest.getRequiredMigrationStep());
@@ -103,6 +94,7 @@ public final class MgmtDistributionSetMapper {
if (distributionSet == null) {
return null;
}
final MgmtDistributionSet response = new MgmtDistributionSet();
MgmtRestModelMapper.mapNamedToNamed(response, distributionSet);
@@ -180,7 +172,6 @@ public final class MgmtDistributionSetMapper {
}
static List<MgmtMetadata> toResponseDsMetadata(final List<DistributionSetMetadata> metadata) {
final List<MgmtMetadata> mappedList = new ArrayList<>(metadata.size());
for (final DistributionSetMetadata distributionSetMetadata : metadata) {
mappedList.add(toResponseDsMetadata(distributionSetMetadata));
@@ -195,4 +186,4 @@ public final class MgmtDistributionSetMapper {
return sets.stream().map(MgmtDistributionSetMapper::toResponse).collect(Collectors.toList());
}
}
}

View File

@@ -17,6 +17,8 @@ import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.api.ApiType;
import org.eclipse.hawkbit.api.ArtifactUrl;
import org.eclipse.hawkbit.api.ArtifactUrlHandler;
@@ -42,12 +44,9 @@ import org.springframework.hateoas.Link;
/**
* A mapper which maps repository model to RESTful model representation and
* back.
*
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class MgmtSoftwareModuleMapper {
private MgmtSoftwareModuleMapper() {
// Utility class
}
private static SoftwareModuleCreate fromRequest(final EntityFactory entityFactory,
final MgmtSoftwareModuleRequestBodyPost smsRest) {
@@ -157,7 +156,6 @@ public final class MgmtSoftwareModuleMapper {
}
static void addLinks(final Artifact artifact, final MgmtArtifact response) {
response.add(linkTo(methodOn(MgmtDownloadArtifactResource.class)
.downloadArtifact(artifact.getSoftwareModule().getId(), artifact.getId())).withRel("download")
.expand());
@@ -165,7 +163,6 @@ public final class MgmtSoftwareModuleMapper {
static void addLinks(final Artifact artifact, final MgmtArtifact response,
final ArtifactUrlHandler artifactUrlHandler, final SystemManagement systemManagement) {
final List<ArtifactUrl> urls = artifactUrlHandler.getUrls(
new URLPlaceholder(systemManagement.getTenantMetadata().getTenant(),
systemManagement.getTenantMetadata().getId(), null, null,
@@ -173,5 +170,4 @@ public final class MgmtSoftwareModuleMapper {
artifact.getId(), artifact.getSha1Hash())), ApiType.MGMT, null);
urls.forEach(entry -> response.add(Link.of(entry.getRef()).withRel(entry.getRel()).expand()));
}
}
}

View File

@@ -1071,8 +1071,8 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
assertThat(distributionSetManagement.count()).isEqualTo(1);
assertThat(set.isLocked()).as("Created distribution set should not be locked").isFalse();
// lock
final String body = new JSONObject().put("locked", true).toString();
mvc.perform(put("/rest/v1/distributionsets/{dsId}", set.getId()).content(body)
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
@@ -1084,8 +1084,8 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
}
@Test
@Description("Tests the unlock. It is verified that the distribution set can't be unmarked as locked through update operation.")
void unlockDistributionSetSkippedSilently() throws Exception {
@Description("Tests the unlock.")
void unlockDistributionSet() throws Exception {
// prepare test data
assertThat(distributionSetManagement.findByCompleted(PAGE, true)).hasSize(0);
@@ -1094,19 +1094,19 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
distributionSetManagement.lock(set.getId());
assertThat(distributionSetManagement.get(set.getId())
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, set.getId())).isLocked())
.as("Created software module should not be locked")
.as("Distribution set should be locked")
.isTrue();
// unlock
final String body = new JSONObject().put("locked", false).toString();
mvc.perform(put("/rest/v1/distributionsets/{dsId}", set.getId()).content(body)
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.locked", equalTo(true)));
.andExpect(jsonPath("$.locked", equalTo(false)));
final DistributionSet updatedSet = distributionSetManagement.get(set.getId()).get();
assertThat(updatedSet.isLocked()).isEqualTo(true);
assertThat(updatedSet.isLocked()).isEqualTo(false);
}
@Test

View File

@@ -202,6 +202,7 @@ class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegrationTes
.pollInterval(10L, TimeUnit.MILLISECONDS)
.until(() -> sm.getLastModifiedAt() > 0L && sm.getLastModifiedBy() != null);
// lock
final String body = new JSONObject().put("locked", true).toString();
final ResultActions resultActions =
mvc.perform(put("/rest/v1/softwaremodules/{smId}", sm.getId()).content(body)
@@ -221,15 +222,15 @@ class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegrationTes
}
@Test
@Description("Tests the unlock. It is verified that the software module can't be unmarked as locked through update operation.")
@Description("Tests the unlock.")
@WithUser(principal = "smUpdateTester", allSpPermissions = true)
void unlockSoftwareModuleSkippedSilently() throws Exception {
void unlockSoftwareModule() throws Exception {
final SoftwareModule sm = softwareModuleManagement.create(
entityFactory.softwareModule().create().type(osType).name("name1").version("version1"));
softwareModuleManagement.lock(sm.getId());
assertThat(softwareModuleManagement.get(sm.getId())
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, sm.getId())).isLocked())
.as("Created software module should not be locked")
.as("Software module is locked")
.isTrue();
// ensures that we are not to fast so that last modified is not set correctly
Awaitility.await()
@@ -237,6 +238,7 @@ class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegrationTes
.pollInterval(10L, TimeUnit.MILLISECONDS)
.until(() -> sm.getLastModifiedAt() > 0L && sm.getLastModifiedBy() != null);
// unlock
final String body = new JSONObject().put("locked", false).toString();
final ResultActions resultActions =
mvc.perform(put("/rest/v1/softwaremodules/{smId}", sm.getId()).content(body)
@@ -244,14 +246,14 @@ class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegrationTes
final SoftwareModule updatedSm = softwareModuleManagement.get(sm.getId()).get();
assertThat(updatedSm.getLastModifiedBy()).isEqualTo("smUpdateTester");
assertThat(updatedSm.isLocked()).isTrue(); // not unlocked
assertThat(updatedSm.isLocked()).isFalse(); // not unlocked
resultActions
.andExpect(status().isOk())
.andExpect(jsonPath("$.id", equalTo(sm.getId().intValue())))
.andExpect(jsonPath("$.lastModifiedBy", equalTo("smUpdateTester")))
.andExpect(jsonPath("$.lastModifiedAt", equalTo(updatedSm.getLastModifiedAt())))
.andExpect(jsonPath("$.locked", equalTo(true)));
.andExpect(jsonPath("$.locked", equalTo(false)));
}
@Test