Fix error for downloading soft deleted artifact binary (#1102)

* download soft deleted artifact throws binary gone exception

Signed-off-by: Stefan Klotz <stefan.klotz@bosch.io>

* add test and update documentation

Signed-off-by: Stefan Klotz <stefan.klotz@bosch.io>

* fix javadoc

Signed-off-by: Stefan Klotz <stefan.klotz@bosch.io>

* test soft deleted artifact has no download link

Signed-off-by: Stefan Klotz <stefan.klotz@bosch.io>
This commit is contained in:
Stefan Klotz
2021-03-24 15:17:34 +01:00
committed by GitHub
parent 41922b6dca
commit 78d784f3c4
7 changed files with 109 additions and 6 deletions

View File

@@ -138,6 +138,12 @@ public enum SpServerError {
SP_ARTIFACT_LOAD_FAILED("hawkbit.server.error.artifact.loadFailed",
"Load of artifact failed with internal server error."),
/**
*
*/
SP_ARTIFACT_BINARY_DELETED("hawkbit.server.error.artifact.binaryDeleted",
"The artifact binary does not exist anymore."),
/**
*
*/

View File

@@ -0,0 +1,55 @@
/**
* Copyright (c) 2021 Bosch.IO GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.exception;
import org.eclipse.hawkbit.exception.AbstractServerRtException;
import org.eclipse.hawkbit.exception.SpServerError;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
/**
* Exception indicating that an artifact's binary does not exist anymore. This
* might be caused due to the soft deletion of a {@link SoftwareModule}.
*
*/
public class ArtifactBinaryNoLongerExistsException extends AbstractServerRtException {
private static final SpServerError THIS_ERROR = SpServerError.SP_ARTIFACT_BINARY_DELETED;
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* Creates a new ArtifactBinaryGoneException error.
*/
public ArtifactBinaryNoLongerExistsException() {
super(THIS_ERROR);
}
/**
* Creates a new ArtifactBinaryGoneException error with cause.
*
* @param cause
* for the exception
*/
public ArtifactBinaryNoLongerExistsException(final Throwable cause) {
super(THIS_ERROR, cause);
}
/**
* Creates a new ArtifactBinaryGoneException error with message.
*
* @param message
* of the error
*/
public ArtifactBinaryNoLongerExistsException(final String message) {
super(message, THIS_ERROR);
}
}

View File

@@ -16,6 +16,7 @@ import org.eclipse.hawkbit.artifact.repository.model.AbstractDbArtifact;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtDownloadArtifactRestApi;
import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
import org.eclipse.hawkbit.repository.exception.ArtifactBinaryNoLongerExistsException;
import org.eclipse.hawkbit.repository.exception.ArtifactBinaryNotFoundException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.model.Artifact;
@@ -63,6 +64,9 @@ public class MgmtDownloadArtifactResource implements MgmtDownloadArtifactRestApi
final SoftwareModule module = softwareModuleManagement.get(softwareModuleId)
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, softwareModuleId));
if (module.isDeleted()) {
throw new ArtifactBinaryNoLongerExistsException();
}
final Artifact artifact = module.getArtifact(artifactId)
.orElseThrow(() -> new EntityNotFoundException(Artifact.class, artifactId));

View File

@@ -117,11 +117,12 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi {
@SuppressWarnings("squid:S3655")
public ResponseEntity<MgmtArtifact> getArtifact(@PathVariable("softwareModuleId") final Long softwareModuleId,
@PathVariable("artifactId") final Long artifactId) {
final SoftwareModule module = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, artifactId);
final MgmtArtifact reponse = MgmtSoftwareModuleMapper.toResponse(module.getArtifact(artifactId).get());
MgmtSoftwareModuleMapper.addLinks(module.getArtifact(artifactId).get(), reponse);
if (!module.isDeleted()) {
MgmtSoftwareModuleMapper.addLinks(module.getArtifact(artifactId).get(), reponse);
}
return ResponseEntity.ok(reponse);
}

View File

@@ -50,6 +50,7 @@ import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.eclipse.hawkbit.rest.json.model.ExceptionInfo;
import org.eclipse.hawkbit.rest.util.JsonBuilder;
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
import org.hamcrest.Matchers;
import org.json.JSONArray;
import org.json.JSONObject;
import org.junit.jupiter.api.BeforeEach;
@@ -391,8 +392,7 @@ public class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegra
// upload
mvc.perform(multipart("/rest/v1/softwaremodules/{smId}/artifacts", sm.getId()).file(file)
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isCreated())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
.andExpect(status().isCreated()).andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
.andExpect(jsonPath("$.hashes.md5", equalTo(md5sum)))
.andExpect(jsonPath("$.hashes.sha1", equalTo(sha1sum)))
.andExpect(jsonPath("$.hashes.sha256", equalTo(sha256sum)))
@@ -435,8 +435,7 @@ public class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegra
final SoftwareModule sm = testdataFactory.createSoftwareModuleOs("sm" + i);
mvc.perform(multipart("/rest/v1/softwaremodules/{smId}/artifacts", sm.getId()).file(file)
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isCreated())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
.andExpect(status().isCreated()).andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
.andExpect(jsonPath("$.hashes.md5", equalTo(md5sum)))
.andExpect(jsonPath("$.hashes.sha1", equalTo(sha1sum)))
.andExpect(jsonPath("$.hashes.sha256", equalTo(sha256sum)))
@@ -518,6 +517,31 @@ public class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegra
"http://localhost/rest/v1/softwaremodules/" + sm.getId() + "/artifacts/" + artifact.getId())));
}
@Test
@Description("Verifies the listing of an artifact that belongs to a soft deleted software module.")
public void getArtifactSoftDeleted() throws Exception {
// prepare data for test
final SoftwareModule sm = testdataFactory.createSoftwareModuleOs("softDeleted");
final Artifact artifact = testdataFactory.createArtifacts(sm.getId()).get(0);
testdataFactory.createDistributionSet(Arrays.asList(sm));
softwareModuleManagement.delete(sm.getId());
// perform test
mvc.perform(get("/rest/v1/softwaremodules/{smId}/artifacts/{artId}", sm.getId(), artifact.getId())
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
.andExpect(jsonPath("$.id", equalTo(artifact.getId().intValue())))
.andExpect(jsonPath("$.size", equalTo((int) artifact.getSize())))
.andExpect(jsonPath("$.hashes.md5", equalTo(artifact.getMd5Hash())))
.andExpect(jsonPath("$.hashes.sha1", equalTo(artifact.getSha1Hash())))
.andExpect(jsonPath("$.hashes.sha256", equalTo(artifact.getSha256Hash())))
.andExpect(jsonPath("$.providedFilename", equalTo(artifact.getFilename())))
.andExpect(jsonPath("$._links", Matchers.not(Matchers.hasKey("download"))))
.andExpect(jsonPath("$._links.self.href",
equalTo(String.format("http://localhost/rest/v1/softwaremodules/%d/artifacts/%d", sm.getId(),
artifact.getId()))));
}
@Test
@Description("Verifies the listing of all artifacts assigned to a software module. That includes the artifact metadata and download links.")
public void getArtifacts() throws Exception {
@@ -562,6 +586,11 @@ public class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegra
final SoftwareModule sm = testdataFactory.createSoftwareModuleOs();
final SoftwareModule smSoftDeleted = testdataFactory.createSoftwareModuleOs("softDeleted");
final Artifact artifactSoftDeleted = testdataFactory.createArtifacts(smSoftDeleted.getId()).get(0);
testdataFactory.createDistributionSet(Arrays.asList(smSoftDeleted));
softwareModuleManagement.delete(smSoftDeleted.getId());
// no artifact available
mvc.perform(get("/rest/v1/softwaremodules/{smId}/artifacts/1234567/download", sm.getId()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
@@ -588,6 +617,10 @@ public class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegra
mvc.perform(delete("/rest/v1/softwaremodules/{smId}/artifacts", sm.getId())).andDo(MockMvcResultPrinter.print())
.andExpect(status().isMethodNotAllowed());
// SM soft deleted
mvc.perform(get("/rest/v1/softwaremodules/{smId}/artifacts/{artifactId}/download", smSoftDeleted.getId(),
artifactSoftDeleted.getId())).andDo(MockMvcResultPrinter.print()).andExpect(status().isGone());
}
@Test

View File

@@ -61,6 +61,7 @@ public class ResponseExceptionHandler {
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ARTIFACT_UPLOAD_FAILED_SHA256_MATCH, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ARTIFACT_UPLOAD_FAILED_MD5_MATCH, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ARTIFACT_DELETE_FAILED, HttpStatus.INTERNAL_SERVER_ERROR);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ARTIFACT_BINARY_DELETED, HttpStatus.GONE);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ARTIFACT_LOAD_FAILED, HttpStatus.INTERNAL_SERVER_ERROR);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_QUOTA_EXCEEDED, HttpStatus.FORBIDDEN);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_FILE_SIZE_QUOTA_EXCEEDED, HttpStatus.FORBIDDEN);

View File

@@ -483,6 +483,9 @@ include::../errors/403.adoc[]
| See <<error-body>>
include::../errors/405.adoc[]
include::../errors/406.adoc[]
| `410 Gone`
| The resource does not exist anymore. The software module might be soft deleted.
|
include::../errors/429.adoc[]
|===