From 160e44f0ef5167e57eed8b5d3aefe111996f3c03 Mon Sep 17 00:00:00 2001 From: Michael Herdt Date: Wed, 12 Jul 2023 15:50:59 +0200 Subject: [PATCH] Extend get module artifacts API by download URL (#1390) * Introduce request parameter to request download URLs when retrieving list of artifacts for a specific software module. * Fix DDI integration test by aligning download path to new config * Make use of mgmt representation mode in sw-module mgmt api * Changed path * refactor test names --- .../hawkbit-test-defaults.properties | 4 +- .../AbstractDDiApiIntegrationTest.java | 10 +- .../mgmt/rest/api/MgmtRestConstants.java | 1 - .../rest/api/MgmtSoftwareModuleRestApi.java | 4 +- .../resource/MgmtSoftwareModuleMapper.java | 10 +- .../resource/MgmtSoftwareModuleResource.java | 34 +++++-- .../MgmtSoftwareModuleResourceTest.java | 96 ++++++++++++++----- .../asciidoc/softwaremodules-api-guide.adoc | 4 + .../SoftwaremodulesDocumentationTest.java | 47 ++++++--- 9 files changed, 152 insertions(+), 58 deletions(-) diff --git a/hawkbit-repository/hawkbit-repository-test/src/main/resources/hawkbit-test-defaults.properties b/hawkbit-repository/hawkbit-repository-test/src/main/resources/hawkbit-test-defaults.properties index ddf7f37b0..e90ec6010 100644 --- a/hawkbit-repository/hawkbit-repository-test/src/main/resources/hawkbit-test-defaults.properties +++ b/hawkbit-repository/hawkbit-repository-test/src/main/resources/hawkbit-test-defaults.properties @@ -43,7 +43,7 @@ hawkbit.artifact.url.protocols.download-http.ip=127.0.0.1 hawkbit.artifact.url.protocols.download-http.protocol=http hawkbit.artifact.url.protocols.download-http.port=8080 hawkbit.artifact.url.protocols.download-http.supports=DMF,DDI -hawkbit.artifact.url.protocols.download-http.ref={protocol}://{hostnameRequest}:{portRequest}/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{artifactFileName} +hawkbit.artifact.url.protocols.download-http.ref={protocol}://{hostnameRequest}:{portRequest}/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{artifactFileName}/download hawkbit.artifact.url.protocols.md5sum-http.rel=md5sum-http hawkbit.artifact.url.protocols.md5sum-http.protocol=${hawkbit.artifact.url.protocols.download-http.protocol} hawkbit.artifact.url.protocols.md5sum-http.hostname=${hawkbit.artifact.url.protocols.download-http.hostname} @@ -57,7 +57,7 @@ hawkbit.artifact.url.protocols.download-cdn-http.ip=127.0.0.1 hawkbit.artifact.url.protocols.download-cdn-http.protocol=http hawkbit.artifact.url.protocols.download-cdn-http.port=8080 hawkbit.artifact.url.protocols.download-cdn-http.supports=MGMT -hawkbit.artifact.url.protocols.download-cdn-http.ref={protocol}://{hostnameRequest}:{portRequest}/rest/v1/softwaremodules/{softwareModuleId}/artifacts/{artifactFileName} +hawkbit.artifact.url.protocols.download-cdn-http.ref={protocol}://download-cdn.com/artifacts/{artifactFileName}/download ## Download URL Generation - END # Quota - START diff --git a/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/AbstractDDiApiIntegrationTest.java b/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/AbstractDDiApiIntegrationTest.java index d14985f27..4ad8f7032 100644 --- a/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/AbstractDDiApiIntegrationTest.java +++ b/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/AbstractDDiApiIntegrationTest.java @@ -208,11 +208,12 @@ public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrat contains(artifact.getSha256Hash()))) .andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[0]._links.download-http.href", contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/" + controllerId - + "/softwaremodules/" + osModuleId + "/artifacts/" + artifact.getFilename()))) + + "/softwaremodules/" + osModuleId + "/artifacts/" + artifact.getFilename() + + "/download"))) .andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[0]._links.md5sum-http.href", contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/" + controllerId + "/softwaremodules/" + osModuleId + "/artifacts/" + artifact.getFilename() - + ".MD5SUM"))) + + "/download.MD5SUM"))) .andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[1].size", contains(ARTIFACT_SIZE))) .andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[1].filename", contains(artifactSignature.getFilename()))) @@ -224,11 +225,12 @@ public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrat contains(artifactSignature.getSha256Hash()))) .andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[1]._links.download-http.href", contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/" + controllerId - + "/softwaremodules/" + osModuleId + "/artifacts/" + artifactSignature.getFilename()))) + + "/softwaremodules/" + osModuleId + "/artifacts/" + artifactSignature.getFilename() + + "/download"))) .andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[1]._links.md5sum-http.href", contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/" + controllerId + "/softwaremodules/" + osModuleId + "/artifacts/" + artifactSignature.getFilename() - + ".MD5SUM"))) + + "/download.MD5SUM"))) .andExpect(jsonPath(prefix + ".chunks[?(@.part=='bApp')].version", contains(ds.findFirstModuleByType(appType).get().getVersion()))) .andExpect(jsonPath(prefix + ".chunks[?(@.part=='bApp')].metadata").doesNotExist()) diff --git a/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtRestConstants.java b/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtRestConstants.java index 8d9e0cdd3..ce47dd613 100644 --- a/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtRestConstants.java +++ b/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtRestConstants.java @@ -276,7 +276,6 @@ public final class MgmtRestConstants { */ public static final String REQUEST_PARAMETER_USE_ARTIFACT_URL_HANDLER = "useartifacturlhandler"; - // constant class, private constructor. private MgmtRestConstants() { diff --git a/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtSoftwareModuleRestApi.java b/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtSoftwareModuleRestApi.java index 2055b07ef..1149557cc 100644 --- a/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtSoftwareModuleRestApi.java +++ b/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtSoftwareModuleRestApi.java @@ -82,7 +82,9 @@ public interface MgmtSoftwareModuleRestApi { @GetMapping(value = MgmtRestConstants.SOFTWAREMODULE_V1_REQUEST_MAPPING + "/{softwareModuleId}/artifacts", produces = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE }) - ResponseEntity> getArtifacts(@PathVariable("softwareModuleId") final Long softwareModuleId); + ResponseEntity> getArtifacts(@PathVariable("softwareModuleId") final Long softwareModuleId, + @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_REPRESENTATION_MODE, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_REPRESENTATION_MODE_DEFAULT) String representationModeParam, + @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_USE_ARTIFACT_URL_HANDLER, required = false) final Boolean useArtifactUrlHandler); /** * Handles the GET request of retrieving a single Artifact meta data diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleMapper.java b/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleMapper.java index 7fdc9f2fa..3f3c52ea9 100644 --- a/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleMapper.java +++ b/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleMapper.java @@ -124,7 +124,7 @@ public final class MgmtSoftwareModuleMapper { } static void addLinks(final SoftwareModule softwareModule, final MgmtSoftwareModule response) { - response.add(linkTo(methodOn(MgmtSoftwareModuleRestApi.class).getArtifacts(response.getModuleId())) + response.add(linkTo(methodOn(MgmtSoftwareModuleRestApi.class).getArtifacts(response.getModuleId(), null, null)) .withRel(MgmtRestConstants.SOFTWAREMODULE_V1_ARTIFACT).expand()); response.add(linkTo( @@ -172,12 +172,4 @@ public final class MgmtSoftwareModuleMapper { urls.forEach(entry -> response.add(Link.of(entry.getRef()).withRel(entry.getRel()).expand())); } - static List artifactsToResponse(final Collection artifacts) { - if (artifacts == null) { - return Collections.emptyList(); - } - - return new ResponseList<>( - artifacts.stream().map(MgmtSoftwareModuleMapper::toResponse).collect(Collectors.toList())); - } } diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResource.java b/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResource.java index 6b8be1b4e..bcd58055a 100644 --- a/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResource.java +++ b/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResource.java @@ -25,6 +25,7 @@ import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.MgmtSoftwareModuleMeta import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.MgmtSoftwareModuleMetadataBodyPut; import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.MgmtSoftwareModuleRequestBodyPost; import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.MgmtSoftwareModuleRequestBodyPut; +import org.eclipse.hawkbit.mgmt.rest.api.MgmtRepresentationMode; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; import org.eclipse.hawkbit.mgmt.rest.api.MgmtSoftwareModuleRestApi; import org.eclipse.hawkbit.repository.ArtifactManagement; @@ -39,6 +40,7 @@ import org.eclipse.hawkbit.repository.model.ArtifactUpload; import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata; import org.eclipse.hawkbit.repository.model.SoftwareModuleType; +import org.eclipse.hawkbit.rest.data.ResponseList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.domain.Page; @@ -105,7 +107,7 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi { fileName = file.getOriginalFilename(); } - try (InputStream in = file.getInputStream()) { + try (final InputStream in = file.getInputStream()) { final Artifact result = artifactManagement.create(new ArtifactUpload(in, softwareModuleId, fileName, md5Sum == null ? null : md5Sum.toLowerCase(), sha1Sum == null ? null : sha1Sum.toLowerCase(), sha256Sum == null ? null : sha256Sum.toLowerCase(), false, file.getContentType(), file.getSize())); @@ -122,10 +124,30 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi { @Override public ResponseEntity> getArtifacts( - @PathVariable("softwareModuleId") final Long softwareModuleId) { - + @PathVariable("softwareModuleId") final Long softwareModuleId, final String representationModeParam, + final Boolean useArtifactUrlHandler) { final SoftwareModule module = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null); - return ResponseEntity.ok(MgmtSoftwareModuleMapper.artifactsToResponse(module.getArtifacts())); + + final boolean isFullMode = parseRepresentationMode(representationModeParam) == MgmtRepresentationMode.FULL; + + final List response = module.getArtifacts().stream().map(artifact -> { + final MgmtArtifact mgmtArtifact = MgmtSoftwareModuleMapper.toResponse(artifact); + if (isFullMode && !module.isDeleted() && Boolean.TRUE.equals(useArtifactUrlHandler)) { + MgmtSoftwareModuleMapper.addLinks(artifact, mgmtArtifact, artifactUrlHandler, systemManagement); + } else if (isFullMode && !module.isDeleted()) { + MgmtSoftwareModuleMapper.addLinks(artifact, mgmtArtifact); + } + return mgmtArtifact; + }).toList(); + return ResponseEntity.ok(new ResponseList<>(response)); + } + + private static MgmtRepresentationMode parseRepresentationMode(final String representationModeParam) { + return MgmtRepresentationMode.fromValue(representationModeParam).orElseGet(() -> { + // no need for a 400, just apply a safe fallback + LOG.warn("Received an invalid representation mode: {}", representationModeParam); + return MgmtRepresentationMode.COMPACT; + }); } @Override @@ -141,7 +163,7 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi { final MgmtArtifact response = MgmtSoftwareModuleMapper.toResponse(module.getArtifact(artifactId).get()); if (!module.isDeleted()) { - if(useArtifactUrlHandler != null && useArtifactUrlHandler) { + if (Boolean.TRUE.equals(useArtifactUrlHandler)) { MgmtSoftwareModuleMapper.addLinks(module.getArtifact(artifactId).get(), response, artifactUrlHandler, systemManagement); } else { @@ -177,7 +199,7 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi { final Pageable pageable = new OffsetBasedPageRequest(sanitizedOffsetParam, sanitizedLimitParam, sorting); final Slice findModulesAll; - long countModulesAll; + final long countModulesAll; if (rsqlParam != null) { findModulesAll = softwareModuleManagement.findByRsql(pageable, rsqlParam); countModulesAll = ((Page) findModulesAll).getTotalElements(); diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResourceTest.java b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResourceTest.java index 338cf5885..234df1310 100644 --- a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResourceTest.java +++ b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResourceTest.java @@ -41,6 +41,7 @@ import org.apache.commons.lang3.RandomStringUtils; import org.awaitility.Awaitility; import org.eclipse.hawkbit.exception.SpServerError; import org.eclipse.hawkbit.mgmt.json.model.artifact.MgmtArtifact; +import org.eclipse.hawkbit.mgmt.rest.api.MgmtRepresentationMode; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; import org.eclipse.hawkbit.repository.Constants; import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException; @@ -64,6 +65,8 @@ import org.json.JSONArray; import org.json.JSONObject; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.springframework.data.domain.PageRequest; import org.springframework.hateoas.MediaTypes; import org.springframework.http.MediaType; @@ -570,14 +573,12 @@ class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegrationTes final int artifactSize = 5 * 1024; final byte random[] = randomBytes(artifactSize); - final Artifact artifact = artifactManagement.create( - new ArtifactUpload(new ByteArrayInputStream(random), sm.getId(), "file1", false, artifactSize)); + final Artifact artifact = artifactManagement + .create(new ArtifactUpload(new ByteArrayInputStream(random), sm.getId(), "file1", false, artifactSize)); // 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()) + 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(random.length))) @@ -586,29 +587,30 @@ class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegrationTes .andExpect(jsonPath("$.hashes.sha256", equalTo(artifact.getSha256Hash()))) .andExpect(jsonPath("$.providedFilename", equalTo("file1"))) .andExpect(jsonPath("$._links.download.href", - equalTo("http://localhost/rest/v1/softwaremodules/" + sm.getId() + "/artifacts/" - + artifact.getId() + "/download"))) - .andExpect(jsonPath("$._links.self.href", equalTo( - "http://localhost/rest/v1/softwaremodules/" + sm.getId() + "/artifacts/" + artifact.getId()))); + equalTo("http://localhost/rest/v1/softwaremodules/%s/artifacts/%s/download" + .formatted(sm.getId(), artifact.getId())))) + .andExpect(jsonPath("$._links.self.href", + equalTo("http://localhost/rest/v1/softwaremodules/%s/artifacts/%s".formatted(sm.getId(), + artifact.getId())))); } - @Test + @ParameterizedTest + @ValueSource(booleans = { true, false }) @Description("Verifies the listing of one defined artifact assigned to a given software module. That includes the artifact metadata and cdn download links.") - void getArtifactWithCdnDownloadUrl() throws Exception { + void getArtifactWithUseArtifactUrlHandlerParameter(final boolean useArtifactUrlHandler) throws Exception { // prepare data for test final SoftwareModule sm = testdataFactory.createSoftwareModuleOs(); final int artifactSize = 5 * 1024; final byte random[] = randomBytes(artifactSize); - final Artifact artifact = artifactManagement.create( - new ArtifactUpload(new ByteArrayInputStream(random), sm.getId(), "file1", false, artifactSize)); + final Artifact artifact = artifactManagement + .create(new ArtifactUpload(new ByteArrayInputStream(random), sm.getId(), "file1", false, artifactSize)); // perform test - mvc.perform(get("/rest/v1/softwaremodules/{smId}/artifacts/{artId}?useartifacturlhandler=true", sm.getId(), artifact.getId()).accept( - MediaType.APPLICATION_JSON)) - .andDo(MockMvcResultPrinter.print()) - .andExpect(status().isOk()) + mvc.perform(get("/rest/v1/softwaremodules/{smId}/artifacts/{artId}", sm.getId(), artifact.getId()) + .param("useartifacturlhandler", String.valueOf(useArtifactUrlHandler)) + .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(random.length))) @@ -616,11 +618,13 @@ class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegrationTes .andExpect(jsonPath("$.hashes.sha1", equalTo(artifact.getSha1Hash()))) .andExpect(jsonPath("$.hashes.sha256", equalTo(artifact.getSha256Hash()))) .andExpect(jsonPath("$.providedFilename", equalTo("file1"))) - .andExpect(jsonPath("$._links.download.href", - equalTo("http://localhost:8080/rest/v1/softwaremodules/" + sm.getId() + "/artifacts/" - + artifact.getFilename()))) + .andExpect(jsonPath("$._links.download.href", useArtifactUrlHandler + ? equalTo("http://download-cdn.com/artifacts/%s/download".formatted(artifact.getFilename())) + : equalTo("http://localhost/rest/v1/softwaremodules/%s/artifacts/%s/download" + .formatted(sm.getId(), artifact.getId())))) .andExpect(jsonPath("$._links.self.href", equalTo( - "http://localhost/rest/v1/softwaremodules/" + sm.getId() + "/artifacts/" + artifact.getId()))); + "http://localhost/rest/v1/softwaremodules/%s/artifacts/%s".formatted(sm.getId(), + artifact.getId())))); } @Test @@ -651,7 +655,7 @@ class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegrationTes } @Test - @Description("Verifies the listing of all artifacts assigned to a software module. That includes the artifact metadata and download links.") + @Description("Verifies the listing of all artifacts assigned to a software module. That includes the artifact metadata.") void getArtifacts() throws Exception { final SoftwareModule sm = testdataFactory.createSoftwareModuleOs(); @@ -685,6 +689,52 @@ class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegrationTes "http://localhost/rest/v1/softwaremodules/" + sm.getId() + "/artifacts/" + artifact2.getId()))); } + @ParameterizedTest + @ValueSource(booleans = { true, false }) + @Description("Verifies the listing of all artifacts assigned to a software module. That includes the artifact metadata and download links.") + void getArtifactsWithUseArtifactUrlHandlerParameter(final boolean useArtifactUrlHandler) throws Exception { + final SoftwareModule sm = testdataFactory.createSoftwareModuleOs(); + + final int artifactSize = 5 * 1024; + final byte[] random = randomBytes(artifactSize); + + final Artifact artifact = artifactManagement + .create(new ArtifactUpload(new ByteArrayInputStream(random), sm.getId(), "file1", false, artifactSize)); + final Artifact artifact2 = artifactManagement + .create(new ArtifactUpload(new ByteArrayInputStream(random), sm.getId(), "file2", false, artifactSize)); + + mvc.perform(get("/rest/v1/softwaremodules/{smId}/artifacts", sm.getId()) + .param("representation", MgmtRepresentationMode.FULL.toString()) + .param("useartifacturlhandler", String.valueOf(useArtifactUrlHandler)) + .accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.[0].id", equalTo(artifact.getId().intValue()))) + .andExpect(jsonPath("$.[0].size", equalTo(random.length))) + .andExpect(jsonPath("$.[0].hashes.md5", equalTo(artifact.getMd5Hash()))) + .andExpect(jsonPath("$.[0].hashes.sha1", equalTo(artifact.getSha1Hash()))) + .andExpect(jsonPath("$.[0].hashes.sha256", equalTo(artifact.getSha256Hash()))) + .andExpect(jsonPath("$.[0].providedFilename", equalTo("file1"))) + .andExpect(jsonPath("$.[0]._links.download.href", useArtifactUrlHandler + ? equalTo("http://download-cdn.com/artifacts/%s/download".formatted(artifact.getFilename())) + : equalTo("http://localhost/rest/v1/softwaremodules/%s/artifacts/%s/download" + .formatted(sm.getId(), artifact.getId())))) + .andExpect(jsonPath("$.[0]._links.self.href", + equalTo("http://localhost/rest/v1/softwaremodules/" + sm.getId() + "/artifacts/" + + artifact.getId()))) + .andExpect(jsonPath("$.[1].id", equalTo(artifact2.getId().intValue()))) + .andExpect(jsonPath("$.[1].hashes.md5", equalTo(artifact2.getMd5Hash()))) + .andExpect(jsonPath("$.[1].hashes.sha1", equalTo(artifact2.getSha1Hash()))) + .andExpect(jsonPath("$.[1].hashes.sha256", equalTo(artifact2.getSha256Hash()))) + .andExpect(jsonPath("$.[1].providedFilename", equalTo("file2"))) + .andExpect(jsonPath("$.[1]._links.download.href", useArtifactUrlHandler + ? equalTo("http://download-cdn.com/artifacts/%s/download".formatted(artifact2.getFilename())) + : equalTo("http://localhost/rest/v1/softwaremodules/%s/artifacts/%s/download" + .formatted(sm.getId(), artifact2.getId())))) + .andExpect(jsonPath("$.[1]._links.self.href", + equalTo("http://localhost/rest/v1/softwaremodules/%s/artifacts/%s".formatted(sm.getId(), + artifact2.getId())))); + } + @Test @Description("Verifies that the system refuses unsupported request types and answers as defined to them, e.g. NOT FOUND on a non existing resource. Or a HTTP POST for updating a resource results in METHOD NOT ALLOWED etc.") void invalidRequestsOnArtifactResource() throws Exception { diff --git a/hawkbit-rest/hawkbit-rest-docs/src/main/asciidoc/softwaremodules-api-guide.adoc b/hawkbit-rest/hawkbit-rest-docs/src/main/asciidoc/softwaremodules-api-guide.adoc index 8a8f237e0..3b6c9368b 100644 --- a/hawkbit-rest/hawkbit-rest-docs/src/main/asciidoc/softwaremodules-api-guide.adoc +++ b/hawkbit-rest/hawkbit-rest-docs/src/main/asciidoc/softwaremodules-api-guide.adoc @@ -271,6 +271,10 @@ include::{snippets}/softwaremodules/get-artifacts/http-request.adoc[] include::{snippets}/softwaremodules/get-artifacts/path-parameters.adoc[] +==== Request query parameter + +include::{snippets}/softwaremodules/get-artifacts-with-parameters/request-parameters.adoc[] + === Response (Status 200) ==== Response fields diff --git a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/SoftwaremodulesDocumentationTest.java b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/SoftwaremodulesDocumentationTest.java index 09d6f4f67..c625a48f6 100644 --- a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/SoftwaremodulesDocumentationTest.java +++ b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/SoftwaremodulesDocumentationTest.java @@ -28,6 +28,7 @@ import java.util.List; import org.apache.commons.lang3.RandomStringUtils; import org.eclipse.hawkbit.im.authentication.SpPermission; +import org.eclipse.hawkbit.mgmt.rest.api.MgmtRepresentationMode; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; import org.eclipse.hawkbit.repository.Constants; import org.eclipse.hawkbit.repository.model.Artifact; @@ -249,7 +250,7 @@ public class SoftwaremodulesDocumentationTest extends AbstractApiRestDocumentati public void getArtifacts() throws Exception { final SoftwareModule sm = testdataFactory.createSoftwareModuleOs(); - final byte random[] = RandomStringUtils.random(5).getBytes(); + final byte[] random = RandomStringUtils.random(5).getBytes(); artifactManagement.create(new ArtifactUpload(new ByteArrayInputStream(random), sm.getId(), "file1", false, 0)); @@ -278,13 +279,36 @@ public class SoftwaremodulesDocumentationTest extends AbstractApiRestDocumentati fieldWithPath("[]._links.self").ignored()))); } + @Test + @Description("Handles the GET request of retrieving all meta data of artifacts assigned to a software module (in full representation mode including a download URL by the artifact provider). Required Permission: " + + SpPermission.READ_REPOSITORY) + public void getArtifactsWithParameters() throws Exception { + final SoftwareModule sm = testdataFactory.createSoftwareModuleOs(); + + final byte[] random = RandomStringUtils.random(5).getBytes(); + + artifactManagement.create(new ArtifactUpload(new ByteArrayInputStream(random), sm.getId(), "file1", false, 0)); + + mockMvc.perform( + get(MgmtRestConstants.SOFTWAREMODULE_V1_REQUEST_MAPPING + "/{softwareModuleId}/artifacts", sm.getId()) + .param("representation", MgmtRepresentationMode.FULL.toString()) + .param("useartifacturlhandler", Boolean.TRUE.toString())) + .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) + .andExpect(content().contentType(MediaTypes.HAL_JSON)) + .andDo(this.document.document(requestParameters( + parameterWithName("representation").description(MgmtApiModelProperties.REPRESENTATION_MODE) + .optional(), + parameterWithName("useartifacturlhandler") + .description(MgmtApiModelProperties.ARTIFACT_DOWNLOAD_USE_URL_HANDLER).optional()))); + } + @Test @Description("Handles POST request for artifact upload. Required Permission: " + SpPermission.CREATE_REPOSITORY) public void postArtifact() throws Exception { final SoftwareModule sm = testdataFactory.createSoftwareModuleOs(); // create test file - final byte random[] = RandomStringUtils.random(5).getBytes(); + final byte[] random = RandomStringUtils.random(5).getBytes(); final MockMultipartFile file = new MockMultipartFile("file", "origFilename", null, random); mockMvc.perform( @@ -320,7 +344,7 @@ public class SoftwaremodulesDocumentationTest extends AbstractApiRestDocumentati final SoftwareModule sm = testdataFactory.createSoftwareModuleOs(); - final byte random[] = RandomStringUtils.random(5).getBytes(); + final byte[] random = RandomStringUtils.random(5).getBytes(); final MockMultipartFile file = new MockMultipartFile("file", "origFilename", null, random); mockMvc.perform( @@ -342,7 +366,7 @@ public class SoftwaremodulesDocumentationTest extends AbstractApiRestDocumentati public void deleteArtifact() throws Exception { final SoftwareModule sm = testdataFactory.createSoftwareModuleOs(); - final byte random[] = RandomStringUtils.random(5).getBytes(); + final byte[] random = RandomStringUtils.random(5).getBytes(); final Artifact artifact = artifactManagement .create(new ArtifactUpload(new ByteArrayInputStream(random), sm.getId(), "file1", false, 0)); @@ -361,7 +385,7 @@ public class SoftwaremodulesDocumentationTest extends AbstractApiRestDocumentati public void getArtifact() throws Exception { final SoftwareModule sm = testdataFactory.createSoftwareModuleOs(); - final byte random[] = RandomStringUtils.random(5).getBytes(); + final byte[] random = RandomStringUtils.random(5).getBytes(); final Artifact artifact = artifactManagement .create(new ArtifactUpload(new ByteArrayInputStream(random), sm.getId(), "file1", false, 0)); @@ -397,19 +421,18 @@ public class SoftwaremodulesDocumentationTest extends AbstractApiRestDocumentati public void getArtifactWithParameters() throws Exception { final SoftwareModule sm = testdataFactory.createSoftwareModuleOs(); - final byte random[] = RandomStringUtils.random(5).getBytes(); + final byte[] random = RandomStringUtils.random(5).getBytes(); final Artifact artifact = artifactManagement .create(new ArtifactUpload(new ByteArrayInputStream(random), sm.getId(), "file1", false, 0)); mockMvc.perform( - get(MgmtRestConstants.SOFTWAREMODULE_V1_REQUEST_MAPPING + "/{softwareModuleId}/artifacts/{artifactId}", - sm.getId(), artifact.getId()).param("useartifacturlhandler", "true")) + get(MgmtRestConstants.SOFTWAREMODULE_V1_REQUEST_MAPPING + "/{softwareModuleId}/artifacts/{artifactId}", + sm.getId(), artifact.getId()).param("useartifacturlhandler", "true")) .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) .andExpect(content().contentType(MediaTypes.HAL_JSON)) - .andDo(this.document.document( - requestParameters( - parameterWithName("useartifacturlhandler").description(MgmtApiModelProperties.ARTIFACT_DOWNLOAD_USE_URL_HANDLER)))); + .andDo(this.document.document(requestParameters(parameterWithName("useartifacturlhandler") + .description(MgmtApiModelProperties.ARTIFACT_DOWNLOAD_USE_URL_HANDLER).optional()))); } @Test @@ -419,7 +442,7 @@ public class SoftwaremodulesDocumentationTest extends AbstractApiRestDocumentati final SoftwareModule sm = testdataFactory.createSoftwareModuleOs(); - final byte random[] = RandomStringUtils.random(5).getBytes(); + final byte[] random = RandomStringUtils.random(5).getBytes(); final Artifact artifact = artifactManagement .create(new ArtifactUpload(new ByteArrayInputStream(random), sm.getId(), "file1", false, 0));