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:
@@ -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."),
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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[]
|
||||
|===
|
||||
|
||||
|
||||
Reference in New Issue
Block a user