JpaSoftwareModule#metadata made map (#2412)

Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2025-05-21 16:59:25 +03:00
committed by GitHub
parent 452d8618d7
commit 64ee8b9906
37 changed files with 413 additions and 895 deletions

View File

@@ -9,11 +9,7 @@
*/
package org.eclipse.hawkbit.repository;
import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.Collections;
import java.util.List;
import java.util.Map.Entry;
import java.util.Optional;
import lombok.Getter;
@@ -28,32 +24,18 @@ public enum SoftwareModuleFields implements RsqlQueryField {
NAME("name"),
DESCRIPTION("description"),
VERSION("version"),
METADATA("metadata", new SimpleImmutableEntry<>("key", "value"));
METADATA("metadata");
private final String jpaEntityFieldName;
private final List<String> subEntityAttributes;
private final Entry<String, String> subEntityMapTuple;
SoftwareModuleFields(final String jpaEntityFieldName) {
this(jpaEntityFieldName, Collections.emptyList(), null);
}
SoftwareModuleFields(final String jpaEntityFieldName, final String... subEntityAttributes) {
this(jpaEntityFieldName, List.of(subEntityAttributes), null);
}
SoftwareModuleFields(final String jpaEntityFieldName, final Entry<String, String> subEntityMapTuple) {
this(jpaEntityFieldName, Collections.emptyList(), subEntityMapTuple);
}
SoftwareModuleFields(final String jpaEntityFieldName, List<String> subEntityAttributes, final Entry<String, String> subEntityMapTuple) {
this.jpaEntityFieldName = jpaEntityFieldName;
this.subEntityMapTuple = subEntityMapTuple;
this.subEntityAttributes = subEntityAttributes;
this.subEntityAttributes = List.of(subEntityAttributes);
}
@Override
public Optional<Entry<String, String>> getSubEntityMapTuple() {
return Optional.ofNullable(subEntityMapTuple);
public boolean isMap() {
return this == METADATA;
}
}

View File

@@ -1,34 +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;
import lombok.Getter;
/**
* Sort fields for SoftwareModuleMetadata.
*/
@Getter
public enum SoftwareModuleMetadataFields implements RsqlQueryField {
KEY("key"),
VALUE("value"),
TARGETVISIBLE("targetVisible");
private final String jpaEntityFieldName;
SoftwareModuleMetadataFields(final String jpaEntityFieldName) {
this.jpaEntityFieldName = jpaEntityFieldName;
}
@Override
public String identifierFieldName() {
return KEY.getJpaEntityFieldName();
}
}

View File

@@ -243,9 +243,9 @@ class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
final Artifact artifactSignature = testdataFactory.createArtifact(nextBytes(ARTIFACT_SIZE),
getOsModule(ds), "test1.signature", ARTIFACT_SIZE);
softwareModuleManagement.updateMetaData(entityFactory.softwareModuleMetadata().create(getOsModule(ds))
softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().create(getOsModule(ds))
.key(visibleMetadataOsKey).value(visibleMetadataOsValue).targetVisible(true));
softwareModuleManagement.updateMetaData(entityFactory.softwareModuleMetadata().create(getOsModule(ds))
softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().create(getOsModule(ds))
.key("metaDataNotVisible").value("withValue").targetVisible(false));
final Target savedTarget = createTargetAndAssertNoActiveActions();
@@ -360,9 +360,9 @@ class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
final Artifact artifactSignature = testdataFactory.createArtifact(
nextBytes(ARTIFACT_SIZE), getOsModule(ds), "test1.signature", ARTIFACT_SIZE);
softwareModuleManagement.updateMetaData(entityFactory.softwareModuleMetadata().create(getOsModule(ds))
softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().create(getOsModule(ds))
.key("metaDataVisible").value("withValue").targetVisible(true));
softwareModuleManagement.updateMetaData(entityFactory.softwareModuleMetadata().create(getOsModule(ds))
softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().create(getOsModule(ds))
.key("metaDataNotVisible").value("withValue").targetVisible(false));
final Target savedTarget = createTargetAndAssertNoActiveActions();

View File

@@ -26,6 +26,7 @@ import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.MgmtSoftwareModuleRequ
import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.MgmtSoftwareModuleRequestBodyPut;
import org.eclipse.hawkbit.rest.json.model.ExceptionInfo;
import org.springframework.hateoas.MediaTypes;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
@@ -37,6 +38,7 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.multipart.MultipartFile;
/**
@@ -55,7 +57,7 @@ public interface MgmtSoftwareModuleRestApi {
* @param optionalFileName to override {@link MultipartFile#getOriginalFilename()}
* @param md5Sum checksum for uploaded content check
* @param sha1Sum checksum for uploaded content check
* @param sha256sum checksum for uploaded content check
* @param sha256Sum checksum for uploaded content check
* @return In case all sets could successful be created the ResponseEntity with status code 201 - Created but without ResponseBody. In any
* failure the JsonResponseExceptionHandler is handling the response.
*/
@@ -387,16 +389,49 @@ public interface MgmtSoftwareModuleRestApi {
@DeleteMapping(value = MgmtRestConstants.SOFTWAREMODULE_V1_REQUEST_MAPPING + "/{softwareModuleId}")
ResponseEntity<Void> deleteSoftwareModule(@PathVariable("softwareModuleId") Long softwareModuleId);
/**
* Creates a list of metadata for a specific software module.
*
* @param softwareModuleId the ID of the distribution set to create metadata for
* @param metadataRest the list of metadata entries to create
* @return status created if post request is successful with the value of the created metadata
*/
@Operation(summary = "Creates a list of metadata for a specific Software Module", description = "Create a list of metadata entries Required Permission: UPDATE_REPOSITORY")
@ApiResponses(value = {
@ApiResponse(responseCode = "201", description = "Successfully created"),
@ApiResponse(responseCode = "400", description = "Bad Request - e.g. invalid parameters",
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ExceptionInfo.class))),
@ApiResponse(responseCode = "401", description = "The request requires user authentication.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "403",
description = "Insufficient permissions, entity is not allowed to be changed (i.e. read-only) or " +
"data volume restriction applies.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "404", description = "Software Module not found", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "406", description = "In case accept header is specified and not application/json.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "409", description = "E.g. in case an entity is created or modified by another " +
"user in another request at the same time. You may retry your modification request.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "415", description = "The request was attempt with a media-type which is not " +
"supported by the server for this resource.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "429", description = "Too many requests. The server will refuse further attempts " +
"and the client has to wait another second.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true)))
})
@PostMapping(value = MgmtRestConstants.SOFTWAREMODULE_V1_REQUEST_MAPPING + "/{softwareModuleId}/metadata",
consumes = { MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE })
@ResponseStatus(HttpStatus.CREATED)
void createMetadata(@PathVariable("softwareModuleId") Long softwareModuleId, @RequestBody List<MgmtSoftwareModuleMetadata> metadataRest);
/**
* Gets a paged list of metadata for a software module.
*
* @param softwareModuleId the ID of the software module for the metadata
* @param pagingOffsetParam the offset of list of metadata for pagination, might not be present in the rest request then default value will
* be applied
* @param pagingLimitParam the limit of the paged request, might not be present in the rest request then default value will be applied
* @param sortParam the sorting parameter in the request URL, syntax {@code field:direction, field:direction}
* @param rsqlParam the search parameter in the request URL, syntax {@code q=key==abc}
* @return status OK if get request is successful with the paged list of metadata
* @return status OK with the paged list of metadata, if the request is successful
*/
@Operation(summary = "Return metadata for a Software Module", description = "Get a paged list of metadata for a software module. Required Permission: READ_REPOSITORY")
@ApiResponses(value = {
@@ -421,30 +456,7 @@ public interface MgmtSoftwareModuleRestApi {
})
@GetMapping(value = MgmtRestConstants.SOFTWAREMODULE_V1_REQUEST_MAPPING + "/{softwareModuleId}/metadata",
produces = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<PagedList<MgmtSoftwareModuleMetadata>> getMetadata(
@PathVariable("softwareModuleId") Long softwareModuleId,
@RequestParam(
value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET,
defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET)
@Schema(description = "The paging offset (default is 0)")
int pagingOffsetParam,
@RequestParam(
value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT,
defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT)
@Schema(description = "The maximum number of entries in a page (default is 50)")
int pagingLimitParam,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false)
@Schema(description = """
The query parameter sort allows to define the sort order for the result of a query. A sort criteria
consists of the name of a field and the sort direction (ASC for ascending and DESC descending).
The sequence of the sort criteria (multiple can be used) defines the sort order of the entities
in the result.""")
String sortParam,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false)
@Schema(description = """
Query fields based on the Feed Item Query Language (FIQL). See Entity Definitions for
available fields.""")
String rsqlParam);
ResponseEntity<PagedList<MgmtSoftwareModuleMetadata>> getMetadata(@PathVariable("softwareModuleId") Long softwareModuleId);
/**
* Gets a single metadata value for a specific key of a software module.
@@ -486,7 +498,6 @@ public interface MgmtSoftwareModuleRestApi {
* @param softwareModuleId the ID of the software module to update the metadata entry
* @param metadataKey the key of the metadata to update the value
* @param metadata body to update
* @return status OK if the update request is successful and the updated metadata result
*/
@Operation(summary = "Update a single metadata value of a Software Module", description = "Update a single metadata value for speficic key. Required Permission: UPDATE_REPOSITORY")
@ApiResponses(value = {
@@ -508,9 +519,9 @@ public interface MgmtSoftwareModuleRestApi {
"and the client has to wait another second.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true)))
})
@PutMapping(value = MgmtRestConstants.SOFTWAREMODULE_V1_REQUEST_MAPPING + "/{softwareModuleId}/metadata/{metadataKey}",
produces = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<MgmtSoftwareModuleMetadata> updateMetadata(
@PutMapping(value = MgmtRestConstants.SOFTWAREMODULE_V1_REQUEST_MAPPING + "/{softwareModuleId}/metadata/{metadataKey}")
@ResponseStatus(HttpStatus.OK)
void updateMetadata(
@PathVariable("softwareModuleId") Long softwareModuleId,
@PathVariable("metadataKey") String metadataKey,
@RequestBody MgmtSoftwareModuleMetadataBodyPut metadata);
@@ -520,7 +531,6 @@ public interface MgmtSoftwareModuleRestApi {
*
* @param softwareModuleId the ID of the software module to delete the metadata entry
* @param metadataKey the key of the metadata to delete
* @return status OK if the delete request is successful
*/
@Operation(summary = "Delete single metadata entry from the software module", description = "Delete a single metadata. Required Permission: UPDATE_REPOSITORY")
@ApiResponses(value = {
@@ -543,47 +553,6 @@ public interface MgmtSoftwareModuleRestApi {
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true)))
})
@DeleteMapping(value = MgmtRestConstants.SOFTWAREMODULE_V1_REQUEST_MAPPING + "/{softwareModuleId}/metadata/{metadataKey}")
ResponseEntity<Void> deleteMetadata(
@PathVariable("softwareModuleId") Long softwareModuleId,
@PathVariable("metadataKey") String metadataKey);
/**
* Creates a list of metadata for a specific software module.
*
* @param softwareModuleId the ID of the distribution set to create metadata for
* @param metadataRest the list of metadata entries to create
* @return status created if post request is successful with the value of the created metadata
*/
@Operation(summary = "Creates a list of metadata for a specific Software Module", description = "Create a list of metadata entries Required Permission: UPDATE_REPOSITORY")
@ApiResponses(value = {
@ApiResponse(responseCode = "201", description = "Successfully created"),
@ApiResponse(responseCode = "400", description = "Bad Request - e.g. invalid parameters",
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ExceptionInfo.class))),
@ApiResponse(responseCode = "401", description = "The request requires user authentication.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "403",
description = "Insufficient permissions, entity is not allowed to be changed (i.e. read-only) or " +
"data volume restriction applies.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "404", description = "Software Module not found", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "406", description = "In case accept header is specified and not application/json.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "409", description = "E.g. in case an entity is created or modified by another " +
"user in another request at the same time. You may retry your modification request.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "415", description = "The request was attempt with a media-type which is not " +
"supported by the server for this resource.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "429", description = "Too many requests. The server will refuse further attempts " +
"and the client has to wait another second.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true)))
})
@PostMapping(value = MgmtRestConstants.SOFTWAREMODULE_V1_REQUEST_MAPPING + "/{softwareModuleId}/metadata",
consumes = { MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE },
produces = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<List<MgmtSoftwareModuleMetadata>> createMetadata(
@PathVariable("softwareModuleId") Long softwareModuleId,
@RequestBody List<MgmtSoftwareModuleMetadata> metadataRest);
@ResponseStatus(HttpStatus.OK)
void deleteMetadata(@PathVariable("softwareModuleId") Long softwareModuleId, @PathVariable("metadataKey") String metadataKey);
}

View File

@@ -33,7 +33,6 @@ import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.builder.DistributionSetCreate;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
import org.eclipse.hawkbit.repository.model.MetaData;
import org.eclipse.hawkbit.rest.json.model.ResponseList;
/**

View File

@@ -117,15 +117,10 @@ public final class MgmtSoftwareModuleMapper {
static void addLinks(final SoftwareModule softwareModule, final MgmtSoftwareModule response) {
response.add(linkTo(methodOn(MgmtSoftwareModuleRestApi.class).getArtifacts(response.getId(), null, null))
.withRel(MgmtRestConstants.SOFTWAREMODULE_V1_ARTIFACT).expand());
response.add(linkTo(
methodOn(MgmtSoftwareModuleTypeRestApi.class).getSoftwareModuleType(softwareModule.getType().getId()))
response.add(linkTo(methodOn(MgmtSoftwareModuleTypeRestApi.class).getSoftwareModuleType(softwareModule.getType().getId()))
.withRel(MgmtRestConstants.SOFTWAREMODULE_V1_TYPE).expand());
response.add(linkTo(methodOn(MgmtSoftwareModuleResource.class).getMetadata(response.getId(),
MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET_VALUE,
MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT_VALUE, null, null)).withRel("metadata")
.expand().expand());
response.add(linkTo(methodOn(MgmtSoftwareModuleResource.class).getMetadata(response.getId()))
.withRel("metadata").expand());
}
static MgmtArtifact toResponse(final Artifact artifact) {

View File

@@ -14,6 +14,7 @@ import java.io.InputStream;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import jakarta.validation.ValidationException;
@@ -234,62 +235,42 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi {
}
@Override
public ResponseEntity<PagedList<MgmtSoftwareModuleMetadata>> getMetadata(
final Long softwareModuleId,
final int pagingOffsetParam, final int pagingLimitParam, final String sortParam, final String rsqlParam) {
public void createMetadata(final Long softwareModuleId, final List<MgmtSoftwareModuleMetadata> metadataRest) {
softwareModuleManagement.createMetadata(MgmtSoftwareModuleMapper.fromRequestSwMetadata(entityFactory, softwareModuleId, metadataRest));
}
@Override
public ResponseEntity<PagedList<MgmtSoftwareModuleMetadata>> getMetadata(final Long softwareModuleId) {
// check if software module exists otherwise throw exception immediately
findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null);
final int sanitizedOffsetParam = PagingUtility.sanitizeOffsetParam(pagingOffsetParam);
final int sanitizedLimitParam = PagingUtility.sanitizePageLimitParam(pagingLimitParam);
final Sort sorting = PagingUtility.sanitizeSoftwareModuleMetadataSortParam(sortParam);
final Pageable pageable = new OffsetBasedPageRequest(sanitizedOffsetParam, sanitizedLimitParam, sorting);
final Page<SoftwareModuleMetadata> metaDataPage;
if (rsqlParam != null) {
metaDataPage = softwareModuleManagement.findMetaDataByRsql(softwareModuleId, rsqlParam, pageable);
} else {
metaDataPage = softwareModuleManagement.findMetaDataBySoftwareModuleId(pageable, softwareModuleId);
}
return ResponseEntity
.ok(new PagedList<>(MgmtSoftwareModuleMapper.toResponseSwMetadata(metaDataPage.getContent()), metaDataPage.getTotalElements()));
final List<SoftwareModuleMetadata> metadata = softwareModuleManagement.getMetadata(softwareModuleId);
return ResponseEntity.ok(new PagedList<>(MgmtSoftwareModuleMapper.toResponseSwMetadata(metadata), metadata.size()));
}
@Override
public ResponseEntity<MgmtSoftwareModuleMetadata> getMetadataValue(final Long softwareModuleId, final String metadataKey) {
final SoftwareModuleMetadata findOne = softwareModuleManagement
.findMetaDataBySoftwareModuleId(softwareModuleId, metadataKey).orElseThrow(
() -> new EntityNotFoundException(SoftwareModuleMetadata.class, softwareModuleId, metadataKey));
final SoftwareModuleMetadata findOne = softwareModuleManagement.getMetadata(softwareModuleId).stream()
.filter(entry -> entry.getKey().equals(metadataKey))
.findFirst()
.orElseThrow(() -> new EntityNotFoundException("SoftwareModule metadata", softwareModuleId + ":" + metadataKey));
return ResponseEntity.ok(MgmtSoftwareModuleMapper.toResponseSwMetadata(findOne));
}
@Override
public ResponseEntity<MgmtSoftwareModuleMetadata> updateMetadata(
final Long softwareModuleId, final String metadataKey, final MgmtSoftwareModuleMetadataBodyPut metadata) {
final SoftwareModuleMetadata updated = softwareModuleManagement
.updateMetaData(entityFactory.softwareModuleMetadata().update(softwareModuleId, metadataKey)
.value(metadata.getValue()).targetVisible(metadata.getTargetVisible()));
return ResponseEntity.ok(MgmtSoftwareModuleMapper.toResponseSwMetadata(updated));
public void updateMetadata(final Long softwareModuleId, final String metadataKey, final MgmtSoftwareModuleMetadataBodyPut metadata) {
softwareModuleManagement.updateMetadata(
entityFactory.softwareModuleMetadata()
.update(softwareModuleId, metadataKey)
.value(metadata.getValue())
.targetVisible(metadata.getTargetVisible()));
}
@Override
@AuditLog(entity = "SoftwareModule", type = AuditLog.Type.DELETE, description = "Delete Software Module Metadata")
public ResponseEntity<Void> deleteMetadata(final Long softwareModuleId, final String metadataKey) {
softwareModuleManagement.deleteMetaData(softwareModuleId, metadataKey);
return ResponseEntity.ok().build();
}
@Override
public ResponseEntity<List<MgmtSoftwareModuleMetadata>> createMetadata(
final Long softwareModuleId, final List<MgmtSoftwareModuleMetadata> metadataRest) {
final List<SoftwareModuleMetadata> created = softwareModuleManagement.putMetaData(
MgmtSoftwareModuleMapper.fromRequestSwMetadata(entityFactory, softwareModuleId, metadataRest));
return ResponseEntity.status(HttpStatus.CREATED).body(MgmtSoftwareModuleMapper.toResponseSwMetadata(created));
public void deleteMetadata(final Long softwareModuleId, final String metadataKey) {
softwareModuleManagement.deleteMetadata(softwareModuleId, metadataKey);
}
private static MgmtRepresentationMode parseRepresentationMode(final String representationModeParam) {

View File

@@ -19,7 +19,6 @@ import org.eclipse.hawkbit.repository.DistributionSetTypeFields;
import org.eclipse.hawkbit.repository.RolloutFields;
import org.eclipse.hawkbit.repository.RolloutGroupFields;
import org.eclipse.hawkbit.repository.SoftwareModuleFields;
import org.eclipse.hawkbit.repository.SoftwareModuleMetadataFields;
import org.eclipse.hawkbit.repository.SoftwareModuleTypeFields;
import org.eclipse.hawkbit.repository.TagFields;
import org.eclipse.hawkbit.repository.TargetFields;
@@ -132,22 +131,6 @@ public final class PagingUtility {
return Sort.by(SortUtility.parse(ActionStatusFields.class, sortParam));
}
public static Sort sanitizeMetadataSortParam(final String sortParam) {
if (sortParam == null) {
// default
return Sort.by(Direction.ASC, SoftwareModuleMetadataFields.KEY.getJpaEntityFieldName());
}
return Sort.by(SortUtility.parse(SoftwareModuleMetadataFields.class, sortParam));
}
public static Sort sanitizeSoftwareModuleMetadataSortParam(final String sortParam) {
if (sortParam == null) {
// default
return Sort.by(Direction.ASC, SoftwareModuleMetadataFields.KEY.getJpaEntityFieldName());
}
return Sort.by(SortUtility.parse(SoftwareModuleMetadataFields.class, sortParam));
}
public static Sort sanitizeRolloutSortParam(final String sortParam) {
if (sortParam == null) {
// default

View File

@@ -10,6 +10,7 @@
package org.eclipse.hawkbit.mgmt.rest.resource;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.Matchers.contains;
@@ -74,7 +75,6 @@ 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;
import org.springframework.mock.web.MockMultipartFile;
@@ -162,7 +162,7 @@ class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegrationTes
final SoftwareModule module = testdataFactory.createDistributionSet("one").findFirstModuleByType(osType).get();
for (int index = 0; index < totalMetadata; index++) {
softwareModuleManagement.updateMetaData(entityFactory.softwareModuleMetadata().create(module.getId())
softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().create(module.getId())
.key(knownKeyPrefix + index).value(knownValuePrefix + index));
}
@@ -182,7 +182,7 @@ class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegrationTes
final SoftwareModule module = testdataFactory.createDistributionSet("one").findFirstModuleByType(osType).get();
for (int index = 0; index < totalMetadata; index++) {
softwareModuleManagement.updateMetaData(entityFactory.softwareModuleMetadata().create(module.getId())
softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().create(module.getId())
.key(knownKeyPrefix + index).value(knownValuePrefix + index));
}
@@ -202,7 +202,7 @@ class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegrationTes
final String knownKey = "knownKey";
final String knownValue = "knownValue";
final SoftwareModule module = testdataFactory.createDistributionSet("one").findFirstModuleByType(osType).get();
softwareModuleManagement.updateMetaData(
softwareModuleManagement.updateMetadata(
entityFactory.softwareModuleMetadata().create(module.getId()).key(knownKey).value(knownValue));
mvc.perform(get(MgmtRestConstants.SOFTWAREMODULE_V1_REQUEST_MAPPING + "/{softwareModuleId}/metadata/{metadataKey}",
@@ -1202,8 +1202,7 @@ class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegrationTes
.andExpect(jsonPath("$.createdBy", equalTo("uploadTester")))
.andExpect(jsonPath("$.createdAt", equalTo(os.getCreatedAt())))
.andExpect(jsonPath("$._links.metadata.href",
equalTo("http://localhost/rest/v1/softwaremodules/" + os.getId()
+ "/metadata?offset=0&limit=50")))
equalTo("http://localhost/rest/v1/softwaremodules/" + os.getId() + "/metadata")))
.andExpect(jsonPath("$._links.type.href",
equalTo("http://localhost/rest/v1/softwaremoduletypes/" + osType.getId())))
.andExpect(jsonPath("$._links.artifacts.href",
@@ -1346,7 +1345,6 @@ class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegrationTes
@Test
@Description("Verifies the successful creation of metadata and the enforcement of the meta data quota.")
void createMetadata() throws Exception {
final String knownKey1 = "knownKey1";
final String knownValue1 = "knownValue1";
final String knownKey2 = "knownKey2";
@@ -1361,19 +1359,12 @@ class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegrationTes
mvc.perform(post("/rest/v1/softwaremodules/{swId}/metadata", sm.getId()).accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON).content(metaData1.toString()))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isCreated())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
.andExpect(jsonPath("[0].key", equalTo(knownKey1)))
.andExpect(jsonPath("[0].value", equalTo(knownValue1)))
.andExpect(jsonPath("[0].targetVisible", equalTo(false)))
.andExpect(jsonPath("[1].key", equalTo(knownKey2)))
.andExpect(jsonPath("[1].value", equalTo(knownValue2)))
.andExpect(jsonPath("[1].targetVisible", equalTo(true)));
.andExpect(status().isCreated());
assertThat(softwareModuleManagement.findMetaDataBySoftwareModuleId(sm.getId(), knownKey1))
.as("Metadata key is wrong").get().extracting(SoftwareModuleMetadata::getValue).isEqualTo(knownValue1);
assertThat(softwareModuleManagement.findMetaDataBySoftwareModuleId(sm.getId(), knownKey2))
.as("Metadata key is wrong").get().extracting(SoftwareModuleMetadata::getValue).isEqualTo(knownValue2);
assertThat(softwareModuleManagement.getMetadata(sm.getId(), knownKey1))
.as("Metadata key is wrong").extracting(SoftwareModuleMetadata::getValue).isEqualTo(knownValue1);
assertThat(softwareModuleManagement.getMetadata(sm.getId(), knownKey2))
.as("Metadata key is wrong").extracting(SoftwareModuleMetadata::getValue).isEqualTo(knownValue2);
// verify quota enforcement
final int maxMetaData = quotaManagement.getMaxMetaDataEntriesPerSoftwareModule();
@@ -1388,11 +1379,8 @@ class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegrationTes
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isForbidden());
// verify that the number of meta data entries has not changed
// (we cannot use the PAGE constant here as it tries to sort by ID)
assertThat(softwareModuleManagement
.findMetaDataBySoftwareModuleId(PageRequest.of(0, Integer.MAX_VALUE), sm.getId()).getTotalElements())
.isEqualTo(metaData1.length());
// verify that the number of meta data entries has not changed (we cannot use the PAGE constant here as it tries to sort by ID)
assertThat(softwareModuleManagement.getMetadata(sm.getId()).size()).isEqualTo(metaData1.length());
}
@@ -1405,7 +1393,7 @@ class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegrationTes
final String updateValue = "valueForUpdate";
final SoftwareModule sm = testdataFactory.createSoftwareModuleOs();
softwareModuleManagement.updateMetaData(
softwareModuleManagement.updateMetadata(
entityFactory.softwareModuleMetadata().create(sm.getId()).key(knownKey).value(knownValue));
final JSONObject jsonObject = new JSONObject().put("key", knownKey)
@@ -1415,13 +1403,9 @@ class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegrationTes
mvc.perform(put("/rest/v1/softwaremodules/{swId}/metadata/{key}", sm.getId(), knownKey).accept(
MediaType.APPLICATION_JSON).contentType(MediaType.APPLICATION_JSON).content(jsonObject.toString()))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
.andExpect(jsonPath("key", equalTo(knownKey)))
.andExpect(jsonPath("value", equalTo(updateValue)));
.andExpect(status().isOk());
final SoftwareModuleMetadata assertDS = softwareModuleManagement.findMetaDataBySoftwareModuleId(sm.getId(),
knownKey).get();
final SoftwareModuleMetadata assertDS = softwareModuleManagement.getMetadata(sm.getId(), knownKey);
assertThat(assertDS.getValue()).as("Metadata is wrong").isEqualTo(updateValue);
assertThat(assertDS.isTargetVisible()).as("target visible is wrong").isTrue();
}
@@ -1433,15 +1417,16 @@ class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegrationTes
final String knownKey = "knownKey";
final String knownValue = "knownValue";
final SoftwareModule sm = testdataFactory.createSoftwareModuleOs();
softwareModuleManagement.updateMetaData(
entityFactory.softwareModuleMetadata().create(sm.getId()).key(knownKey).value(knownValue));
final long smId = testdataFactory.createSoftwareModuleOs().getId();
softwareModuleManagement.updateMetadata(
entityFactory.softwareModuleMetadata().create(smId).key(knownKey).value(knownValue));
mvc.perform(delete("/rest/v1/softwaremodules/{swId}/metadata/{key}", sm.getId(), knownKey))
mvc.perform(delete("/rest/v1/softwaremodules/{swId}/metadata/{key}", smId, knownKey))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk());
assertThat(softwareModuleManagement.findMetaDataBySoftwareModuleId(sm.getId(), knownKey)).isNotPresent();
assertThatExceptionOfType(EntityNotFoundException.class)
.isThrownBy(() -> softwareModuleManagement.getMetadata(smId, knownKey));
}
@Test
@@ -1451,11 +1436,11 @@ class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegrationTes
final String knownKey = "knownKey";
final String knownValue = "knownValue";
final SoftwareModule sm = testdataFactory.createSoftwareModuleOs();
softwareModuleManagement.updateMetaData(
entityFactory.softwareModuleMetadata().create(sm.getId()).key(knownKey).value(knownValue));
final long smId = testdataFactory.createSoftwareModuleOs().getId();
softwareModuleManagement.updateMetadata(
entityFactory.softwareModuleMetadata().create(smId).key(knownKey).value(knownValue));
mvc.perform(delete("/rest/v1/softwaremodules/{swId}/metadata/XXX", sm.getId(), knownKey))
mvc.perform(delete("/rest/v1/softwaremodules/{swId}/metadata/XXX", smId, knownKey))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isNotFound());
@@ -1463,7 +1448,7 @@ class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegrationTes
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isNotFound());
assertThat(softwareModuleManagement.findMetaDataBySoftwareModuleId(sm.getId(), knownKey)).isPresent();
assertThat(softwareModuleManagement.getMetadata(smId, knownKey)).isNotNull();
}
@Test
@@ -1474,32 +1459,6 @@ class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegrationTes
.andExpect(status().isNotFound());
}
@Test
@Description("Verifies the successful search of a metadata entry based on value.")
void searchSoftwareModuleMetadataRsql() throws Exception {
final int totalMetadata = 10;
final String knownKeyPrefix = "knownKey";
final String knownValuePrefix = "knownValue";
final SoftwareModule sm = testdataFactory.createSoftwareModuleOs();
for (int index = 0; index < totalMetadata; index++) {
softwareModuleManagement.updateMetaData(entityFactory.softwareModuleMetadata()
.create(sm.getId())
.key(knownKeyPrefix + index)
.value(knownValuePrefix + index));
}
final String rsqlSearchValue1 = "value==knownValue1";
mvc.perform(get("/rest/v1/softwaremodules/{swId}/metadata?q=" + rsqlSearchValue1, sm.getId()))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(jsonPath("size", equalTo(1)))
.andExpect(jsonPath("total", equalTo(1)))
.andExpect(jsonPath("content[0].key", equalTo("knownKey1")))
.andExpect(jsonPath("content[0].value", equalTo("knownValue1")));
}
private void assertArtifact(final SoftwareModule sm, final byte[] random) throws IOException {
// check result in db...
// repo

View File

@@ -32,7 +32,6 @@ import org.eclipse.hawkbit.repository.exception.UnsupportedSoftwareModuleForThis
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetFilter;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.MetaData;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Statistic;
import org.springframework.data.domain.Page;
@@ -126,12 +125,11 @@ public interface DistributionSetManagement extends RepositoryManagement<Distribu
/**
* Creates a map of distribution set meta-data entries.
*
* @param id if the {@link DistributionSet} the metadata has to be created for
* @param id if the {@link DistributionSet} the meta-data has to be created for
* @param metadata the meta-data entries to create or update
* @throws EntityNotFoundException if given set does not exist
* @throws EntityAlreadyExistsException in case one of the meta-data entry already exists for the specific key
* @throws AssignmentQuotaExceededException if the maximum number of {@link MetaData} entries is exceeded for the addressed
* {@link DistributionSet}
* @throws AssignmentQuotaExceededException if the maximum number of meta-data entries is exceeded for the addressed {@link DistributionSet}
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
void createMetadata(long id, @NotEmpty Map<String, String> metadata);

View File

@@ -22,7 +22,6 @@ import org.eclipse.hawkbit.repository.builder.TargetBuilder;
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryBuilder;
import org.eclipse.hawkbit.repository.builder.TargetTypeBuilder;
import org.eclipse.hawkbit.repository.model.BaseEntity;
import org.eclipse.hawkbit.repository.model.MetaData;
/**
* central {@link BaseEntity} generation service. Objects are created but not

View File

@@ -26,8 +26,6 @@ import org.eclipse.hawkbit.repository.builder.SoftwareModuleUpdate;
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
@@ -53,7 +51,39 @@ public interface SoftwareModuleManagement extends RepositoryManagement<SoftwareM
* {@link SoftwareModule}
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
List<SoftwareModuleMetadata> putMetaData(@NotNull @Valid Collection<SoftwareModuleMetadataCreate> metadata);
void createMetadata(@NotNull @Valid Collection<SoftwareModuleMetadataCreate> metadata);
/**
* Finds all meta-data by the given software module id.
*
* @param id the software module id to retrieve the meta-data from
* @return a paged result of all meta-data entries for a given software module id
* @throws EntityNotFoundException if software module with given ID does not exist
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
List<SoftwareModuleMetadata> getMetadata(long id);
/**
* Finds all meta-data by the given software module id and key.
*
* @param id the software module id to retrieve the meta-data from
* @param key the meta-data key to retrieve
* @return a paged result of all meta-data entries for a given software module id
* @throws EntityNotFoundException if software module with given ID does not exist ot the
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
SoftwareModuleMetadata getMetadata(long id, String key);
/**
* Finds all meta-data by the given software module id where {@link SoftwareModuleMetadata#isTargetVisible()}.
*
* @param pageable the page request to page the result
* @param id the software module id to retrieve the meta-data from
* @return a paged result of all meta-data entries for a given software module id
* @throws EntityNotFoundException if software module with given ID does not exist
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
Page<SoftwareModuleMetadata> findMetaDataBySoftwareModuleIdAndTargetVisible(@NotNull Pageable pageable, long id);
/**
* Creates or updates a single software module meta-data entry.
@@ -66,7 +96,7 @@ public interface SoftwareModuleManagement extends RepositoryManagement<SoftwareM
* entries is exceeded for the addressed {@link SoftwareModule}
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
SoftwareModuleMetadata updateMetaData(@NotNull @Valid SoftwareModuleMetadataCreate metadata);
SoftwareModuleMetadata updateMetadata(@NotNull @Valid SoftwareModuleMetadataCreate metadata);
/**
* Updates a distribution set meta-data value if corresponding entry exists.
@@ -76,17 +106,18 @@ public interface SoftwareModuleManagement extends RepositoryManagement<SoftwareM
* @throws EntityNotFoundException in case the meta-data entry does not exist and cannot be updated
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
SoftwareModuleMetadata updateMetaData(@NotNull @Valid SoftwareModuleMetadataUpdate update);
SoftwareModuleMetadata updateMetadata(@NotNull @Valid SoftwareModuleMetadataUpdate update);
/**
* Deletes a software module meta-data entry.
*
* @param id where meta-data has to be deleted
* @param key of the meta-data element
* @throws EntityNotFoundException of module or metadata entry does not exist
* @throws EntityNotFoundException if software module with given ID does not exist or the key is not found
* @return true if really deleted, false if not
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
void deleteMetaData(long id, @NotEmpty String key);
void deleteMetadata(long id, @NotEmpty String key);
/**
* Locks a software module.
@@ -144,54 +175,6 @@ public interface SoftwareModuleManagement extends RepositoryManagement<SoftwareM
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
Optional<SoftwareModule> findByNameAndVersionAndType(@NotEmpty String name, @NotEmpty String version, long typeId);
/**
* Finds a single software module meta-data by its id.
*
* @param id where meta-data has to be found
* @param key of the meta-data element
* @return the found SoftwareModuleMetadata or {@code null} if not exits
* @throws EntityNotFoundException is module with given ID does not exist
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
Optional<SoftwareModuleMetadata> findMetaDataBySoftwareModuleId(long id, @NotEmpty String key);
/**
* Finds all meta-data by the given software module id.
*
* @param pageable the page request to page the result
* @param id the software module id to retrieve the meta-data from
* @return a paged result of all meta-data entries for a given software module id
* @throws EntityNotFoundException if software module with given ID does not exist
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
Page<SoftwareModuleMetadata> findMetaDataBySoftwareModuleId(@NotNull Pageable pageable, long id);
/**
* Finds all meta-data by the given software module id where {@link SoftwareModuleMetadata#isTargetVisible()}.
*
* @param pageable the page request to page the result
* @param id the software module id to retrieve the meta-data from
* @return a paged result of all meta-data entries for a given software module id
* @throws EntityNotFoundException if software module with given ID does not exist
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
Page<SoftwareModuleMetadata> findMetaDataBySoftwareModuleIdAndTargetVisible(@NotNull Pageable pageable, long id);
/**
* Finds all meta-data by the given software module id.
*
* @param id the software module id to retrieve the meta-data from
* @param rsqlParam filter definition in RSQL syntax
* @param pageable the page request to page the result
* @return a paged result of all meta-data entries for a given software module id
* @throws RSQLParameterUnsupportedFieldException if a field in the RSQL string is used but not provided by the given
* {@code fieldNameProvider}
* @throws RSQLParameterSyntaxException if the RSQL syntax is wrong
* @throws EntityNotFoundException if software module with given ID does not exist
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
Page<SoftwareModuleMetadata> findMetaDataByRsql(long id, @NotNull String rsqlParam, @NotNull Pageable pageable);
/**
* Retrieves the {@link SoftwareModule}s by their {@link SoftwareModuleType}
*
@@ -215,14 +198,4 @@ public interface SoftwareModuleManagement extends RepositoryManagement<SoftwareM
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
long countByAssignedTo(long distributionSetId);
/**
* Counts all meta-data by the given software module id.
*
* @param id the software module id to retrieve the meta-data count from
* @return count of all meta-data entries for a given software module id
* @throws EntityNotFoundException if software module with given ID does not exist
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
long countMetaDataBySoftwareModuleId(long id);
}

View File

@@ -31,7 +31,6 @@ import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.MetaData;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
@@ -521,15 +520,12 @@ public interface TargetManagement {
@NotNull String controllerId, long distributionSetId, @NotNull String targetFilterQuery);
/**
* Initiates {@link TargetType} assignment to given {@link Target}s. If some
* targets in the list have the {@link TargetType} not yet assigned, they will
* get assigned. If all targets are already of that type, there will be no
* un-assignment.
* Initiates {@link TargetType} assignment to given {@link Target}s. If some targets in the list have the {@link TargetType}
* not yet assigned, they will get assigned. If all targets are already of that type, there will be no un-assignment.
*
* @param controllerIds to set the type to
* @param typeId to assign targets to
* @return {@link TargetTypeAssignmentResult} with all metadata of the
* assignment outcome.
* @return {@link TargetTypeAssignmentResult} with all meta-data of the assignment outcome.
* @throws EntityNotFoundException if target type with given id does not exist
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
@@ -539,7 +535,7 @@ public interface TargetManagement {
* Initiates {@link TargetType} un-assignment to given {@link Target}s. The type of the targets will be set to {@code null}
*
* @param controllerIds to remove the type from
* @return {@link TargetTypeAssignmentResult} with all metadata of the assignment outcome.
* @return {@link TargetTypeAssignmentResult} with all meta-data of the assignment outcome.
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
TargetTypeAssignmentResult unassignType(@NotEmpty Collection<String> controllerIds);
@@ -701,11 +697,11 @@ public interface TargetManagement {
/**
* Creates a list of target meta-data entries.
*
* @param controllerId {@link Target} controller id the metadata has to be created for
* @param controllerId {@link Target} controller id the meta-data has to be created for
* @param metadata the meta-data entries to create or update
* @throws EntityNotFoundException if given target does not exist
* @throws EntityAlreadyExistsException in case one of the metadata entry already exists for the specific key
* @throws AssignmentQuotaExceededException if the maximum number of {@link MetaData} entries is exceeded for the addressed {@link Target}
* @throws EntityAlreadyExistsException in case one of the metad-ata entry already exists for the specific key
* @throws AssignmentQuotaExceededException if the maximum number of meta-data entries is exceeded for the addressed {@link Target}
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
void createMetadata(@NotEmpty String controllerId, @NotEmpty Map<String, String> metadata);
@@ -714,7 +710,7 @@ public interface TargetManagement {
* Finds a single target meta-data by its id.
*
* @param controllerId of the {@link Target}
* @return the found target metadata
* @return the found target meta-data
* @throws EntityNotFoundException if target with given ID does not exist
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
@@ -723,10 +719,10 @@ public interface TargetManagement {
/**
* Updates a target meta-data value if corresponding entry exists.
*
* @param controllerId {@link Target} controller id of the metadata entry to be updated
* @param controllerId {@link Target} controller id of the meta-data entry to be updated
* @param key meta data-entry key to be updated
* @param value meta data-entry to be new value
* @throws EntityNotFoundException in case the metadata entry does not exist and cannot be updated
* @throws EntityNotFoundException in case the meta-data entry does not exist and cannot be updated
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
void updateMetadata(@NotEmpty String controllerId, @NotNull String key, @NotNull String value);
@@ -734,7 +730,7 @@ public interface TargetManagement {
/**
* Deletes a target meta data entry.
*
* @param controllerId where metadata has to be deleted
* @param controllerId where meta-data has to be deleted
* @param key of the meta data element
* @throws EntityNotFoundException if given target does not exist
*/

View File

@@ -9,7 +9,6 @@
*/
package org.eclipse.hawkbit.repository.builder;
import org.eclipse.hawkbit.repository.model.MetaData;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
@@ -19,14 +18,14 @@ import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
public interface SoftwareModuleMetadataBuilder {
/**
* @param softwareModuleId of the {@link SoftwareModule} the {@link MetaData} belongs to
* @param key of {@link MetaData#getKey()}
* @param softwareModuleId of the {@link SoftwareModule} the {@link SoftwareModuleMetadata} belongs to
* @param key of {@link SoftwareModuleMetadata#getKey()}
* @return builder instance
*/
SoftwareModuleMetadataUpdate update(long softwareModuleId, String key);
/**
* @param softwareModuleId of the {@link SoftwareModule} the {@link MetaData} belongs to
* @param softwareModuleId of the {@link SoftwareModule} the {@link SoftwareModuleMetadata} belongs to
* @return builder instance
*/
SoftwareModuleMetadataCreate create(long softwareModuleId);

View File

@@ -13,7 +13,7 @@ import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import org.eclipse.hawkbit.repository.model.BaseEntity;
import org.eclipse.hawkbit.repository.model.MetaData;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
/**
@@ -24,16 +24,16 @@ import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
public interface SoftwareModuleMetadataCreate {
/**
* @param key for {@link MetaData#getKey()}
* @param key for {@link SoftwareModuleMetadata#getKey()}
* @return updated builder instance
*/
SoftwareModuleMetadataCreate key(@Size(min = 1, max = MetaData.KEY_MAX_SIZE) @NotNull String key);
SoftwareModuleMetadataCreate key(@Size(min = 1, max =SoftwareModule.METADATA_KEY_MAX_SIZE) @NotNull String key);
/**
* @param value for {@link MetaData#getValue()}
* @param value for {@link SoftwareModuleMetadata#getValue()}
* @return updated builder instance
*/
SoftwareModuleMetadataCreate value(@Size(max = MetaData.VALUE_MAX_SIZE) String value);
SoftwareModuleMetadataCreate value(@Size(max = SoftwareModule.METADATA_VALUE_MAX_SIZE) String value);
/**
* @param visible for {@link SoftwareModuleMetadata#isTargetVisible()}

View File

@@ -12,7 +12,7 @@ package org.eclipse.hawkbit.repository.builder;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import org.eclipse.hawkbit.repository.model.MetaData;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
/**
@@ -22,10 +22,10 @@ import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
public interface SoftwareModuleMetadataUpdate {
/**
* @param value for {@link MetaData#getValue()}
* @param value for {@link SoftwareModuleMetadata#getValue()}
* @return updated builder instance
*/
SoftwareModuleMetadataUpdate value(@Size(min = 1, max = MetaData.VALUE_MAX_SIZE) @NotNull String value);
SoftwareModuleMetadataUpdate value(@Size(min = 1, max = SoftwareModule.METADATA_VALUE_MAX_SIZE) @NotNull String value);
/**
* @param visible for {@link SoftwareModuleMetadata#isTargetVisible()}

View File

@@ -18,7 +18,6 @@ import lombok.Getter;
import org.eclipse.hawkbit.exception.AbstractServerRtException;
import org.eclipse.hawkbit.exception.SpServerError;
import org.eclipse.hawkbit.repository.model.BaseEntity;
import org.eclipse.hawkbit.repository.model.MetaData;
/**
* the {@link EntityNotFoundException} is thrown when a entity queried but not found.
@@ -76,9 +75,7 @@ public class EntityNotFoundException extends AbstractServerRtException {
* @param entityId of the {@link BaseEntity}
*/
public EntityNotFoundException(final String type, final Object entityId) {
super(type + " with given identifier {" + entityId + "} does not exist.",
THIS_ERROR,
Map.of(TYPE, type, ENTITY_ID, entityId));
super(type + " with given identifier {" + entityId + "} does not exist.", THIS_ERROR, Map.of(TYPE, type, ENTITY_ID, entityId));
}
/**
@@ -91,30 +88,6 @@ public class EntityNotFoundException extends AbstractServerRtException {
this(type.getSimpleName(), entityId);
}
/**
* Parameterized constructor for {@link MetaData} not found.
*
* @param type of the entity that was not found
* @param entityId of the {@link BaseEntity} the {@link MetaData} was for
* @param key for the {@link MetaData} entry
*/
public EntityNotFoundException(final Class<? extends MetaData> type, final Long entityId, final String key) {
this(type, String.valueOf(entityId), key);
}
/**
* Parameterized constructor for {@link MetaData} not found.
*
* @param type of the entity that was not found
* @param entityId of the {@link BaseEntity} the {@link MetaData} was for
* @param key for the {@link MetaData} entry
*/
public EntityNotFoundException(final Class<? extends MetaData> type, final String entityId, final String key) {
super(type.getSimpleName() + " for given entity {" + toEntityString(entityId) + "} and with key {" + key + "} does not exist.",
THIS_ERROR,
Map.of(TYPE, type.getSimpleName(), ENTITY_ID, entityId, KEY, key));
}
/**
* Parameterized constructor for a list of {@link BaseEntity}s not found.
*
@@ -122,10 +95,14 @@ public class EntityNotFoundException extends AbstractServerRtException {
* @param expected collection of the {@link BaseEntity#getId()}s
* @param found collection of the {@link BaseEntity#getId()}s
*/
public EntityNotFoundException(final Class<? extends BaseEntity> type, final Collection<?> expected,
final Collection<?> found) {
super(type.getSimpleName() + "s with given identifiers {" + toEntityString(expected.stream().filter(id -> !found.contains(id))
.map(String::valueOf).collect(Collectors.joining(","))) + "} do not exist.",
public EntityNotFoundException(final Class<? extends BaseEntity> type, final Collection<?> expected, final Collection<?> found) {
super(
String.format("%ss with given identifiers {%s} do not exist.",
type.getSimpleName(),
toEntityString(expected.stream()
.filter(id -> !found.contains(id))
.map(String::valueOf)
.collect(Collectors.joining(",")))),
THIS_ERROR,
Map.of(TYPE, type.getSimpleName(), ENTITY_ID, expected.stream().filter(id -> !found.contains(id)).map(String::valueOf)));
}

View File

@@ -26,12 +26,12 @@ public interface DistributionSet extends NamedVersionedEntity {
/**
* Maximum length of metadata key.
*/
int METADATA_KEY_SIZE = 128;
int METADATA_MAX_KEY_SIZE = 128;
/**
* Maximum length of metadata value.
*/
int METADATA_VALUE_SIZE = 4000;
int METADATA_MAX_VALUE_SIZE = 4000;
/**
* @return type of the {@link DistributionSet}.

View File

@@ -1,44 +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.model;
import java.io.Serializable;
/**
* Meta data for entities, a (key/value) store.
*/
public interface MetaData extends Serializable {
/**
* Maximum length of metadata key.
*/
int KEY_MAX_SIZE = 128;
/**
* Maximum length of metadata value.
*/
int VALUE_MAX_SIZE = 4000;
/**
* @return {@link BaseEntity#getId()} the metadata is related to
*/
Long getEntityId();
/**
* @return the key
*/
String getKey();
/**
* @return the value
*/
String getValue();
}

View File

@@ -22,6 +22,16 @@ public interface SoftwareModule extends NamedVersionedEntity {
*/
int VENDOR_MAX_SIZE = 256;
/**
* Maximum length of metadata key.
*/
int METADATA_KEY_MAX_SIZE = 128;
/**
* Maximum length of metadata value.
*/
int METADATA_VALUE_MAX_SIZE = 4000;
/**
* @return the type of the software module
*/

View File

@@ -9,24 +9,26 @@
*/
package org.eclipse.hawkbit.repository.model;
import java.io.Serializable;
/**
* {@link MetaData} element of a {@link SoftwareModule}.
* Metadata element of a {@link SoftwareModule}. The software module metadata is not only (key, value) pair (like the metadata of
* targets and distribution sets), but also contains the information if the metadata is visible for targets as part of {@link Action}.
*/
public interface SoftwareModuleMetadata extends MetaData {
public interface SoftwareModuleMetadata extends Serializable {
/**
* @return {@link SoftwareModule} this entry belongs to.
* @return the key
*/
SoftwareModule getSoftwareModule();
@Override
default Long getEntityId() {
return getSoftwareModule().getId();
}
String getKey();
/**
* @return <code>true</code> if element is visible for targets as part of
* {@link Action}.
* @return the value
*/
String getValue();
/**
* @return <code>true</code> if element is visible for targets as part of {@link Action}.
*/
boolean isTargetVisible();
}

View File

@@ -40,22 +40,22 @@ public interface Target extends NamedEntity {
/**
* Maximum length of key of controller attribute
*/
int CONTROLLER_ATTRIBUTE_KEY_SIZE = 128;
int CONTROLLER_ATTRIBUTE_MAX_KEY_SIZE = 128;
/**
* Maximum length of value of controller attribute
*/
int CONTROLLER_ATTRIBUTE_VALUE_SIZE = 128;
int CONTROLLER_ATTRIBUTE_MAX_VALUE_SIZE = 128;
/**
* Maximum length of metadata key.
*/
int CONTROLLER_METADATA_KEY_SIZE = 128;
int METADATA_MAX_KEY_SIZE = 128;
/**
* Maximum length of metadata value.
*/
int CONTROLLER_METADATA_VALUE_SIZE = 4000;
int METADATA_MAX_VALUE_SIZE = 4000;
/**
* @return business identifier of the {@link Target}

View File

@@ -26,7 +26,6 @@ import org.eclipse.hawkbit.repository.jpa.builder.JpaActionStatusBuilder;
import org.eclipse.hawkbit.repository.jpa.builder.JpaRolloutGroupBuilder;
import org.eclipse.hawkbit.repository.jpa.builder.JpaSoftwareModuleTypeBuilder;
import org.eclipse.hawkbit.repository.jpa.builder.JpaTagBuilder;
import org.eclipse.hawkbit.repository.model.MetaData;
import org.springframework.validation.annotation.Validated;
/**

View File

@@ -11,8 +11,8 @@ 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.FINISHED;
import static org.eclipse.hawkbit.repository.model.Target.CONTROLLER_ATTRIBUTE_KEY_SIZE;
import static org.eclipse.hawkbit.repository.model.Target.CONTROLLER_ATTRIBUTE_VALUE_SIZE;
import static org.eclipse.hawkbit.repository.model.Target.CONTROLLER_ATTRIBUTE_MAX_KEY_SIZE;
import static org.eclipse.hawkbit.repository.model.Target.CONTROLLER_ATTRIBUTE_MAX_VALUE_SIZE;
import java.net.URI;
import java.time.Duration;
@@ -618,11 +618,11 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
}
private static boolean isAttributeKeyValid(final String key) {
return key != null && key.length() <= CONTROLLER_ATTRIBUTE_KEY_SIZE && PATTERN.matcher(key).matches();
return key != null && key.length() <= CONTROLLER_ATTRIBUTE_MAX_KEY_SIZE && PATTERN.matcher(key).matches();
}
private static boolean isAttributeValueValid(final String value) {
return value == null || (value.length() <= CONTROLLER_ATTRIBUTE_VALUE_SIZE && PATTERN.matcher(value).matches());
return value == null || (value.length() <= CONTROLLER_ATTRIBUTE_MAX_VALUE_SIZE && PATTERN.matcher(value).matches());
}
private static void copy(final Map<String, String> src, final Map<String, String> trg) {

View File

@@ -10,7 +10,6 @@
package org.eclipse.hawkbit.repository.jpa.management;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
@@ -19,7 +18,6 @@ import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jakarta.persistence.EntityManager;
@@ -29,7 +27,6 @@ import org.eclipse.hawkbit.repository.QuotaManagement;
import org.eclipse.hawkbit.repository.RepositoryConstants;
import org.eclipse.hawkbit.repository.SoftwareModuleFields;
import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
import org.eclipse.hawkbit.repository.SoftwareModuleMetadataFields;
import org.eclipse.hawkbit.repository.builder.GenericSoftwareModuleMetadataUpdate;
import org.eclipse.hawkbit.repository.builder.GenericSoftwareModuleUpdate;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleCreate;
@@ -272,53 +269,61 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<SoftwareModuleMetadata> putMetaData(final Collection<SoftwareModuleMetadataCreate> create) {
if (!create.isEmpty()) {
// check if all metadata entries refer to the same software module
final Long id = ((JpaSoftwareModuleMetadataCreate) create.iterator().next()).getSoftwareModuleId();
if (createJpaMetadataCreateStream(create).allMatch(c -> id.equals(c.getSoftwareModuleId()))) {
assertSoftwareModuleExists(id);
assertMetaDataQuota(id, 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).toList();
} else {
// group by software module id to minimize database access
final Map<Long, List<JpaSoftwareModuleMetadataCreate>> groups = createJpaMetadataCreateStream(create)
.collect(Collectors.groupingBy(JpaSoftwareModuleMetadataCreate::getSoftwareModuleId));
return groups.entrySet().stream().flatMap(e -> {
final List<JpaSoftwareModuleMetadataCreate> group = e.getValue();
public void createMetadata(final Collection<SoftwareModuleMetadataCreate> create) {
// group by software module id to minimize database access
create.stream()
.map(JpaSoftwareModuleMetadataCreate.class::cast)
.collect(Collectors.groupingBy(JpaSoftwareModuleMetadataCreate::getSoftwareModuleId))
.forEach((id, createsForSoftwareModule) -> {
assertSoftwareModuleExists(id);
assertMetaDataQuota(e.getKey(), group.size());
assertMetadataQuota(id, createsForSoftwareModule.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);
}).toList();
}
}
JpaManagementHelper.touch(
entityManager, softwareModuleRepository,
(JpaSoftwareModule) get(id).orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id)));
createsForSoftwareModule.forEach(this::saveMetadata);
});
}
return Collections.emptyList();
@SuppressWarnings({"unchecked", "rawtypes"})
@Override
public List<SoftwareModuleMetadata> getMetadata(final long id) {
assertSoftwareModuleExists(id);
return (List)softwareModuleMetadataRepository.findAll(metadataBySoftwareModuleIdSpec(id));
}
@Override
public SoftwareModuleMetadata getMetadata(final long id, final String key) {
assertSoftwareModuleExists(id);
return findMetadata(id, key).orElseThrow(() -> new EntityNotFoundException("SoftwareModuleMetadata", id + ":" + key));
}
@Override
public Page<SoftwareModuleMetadata> findMetaDataBySoftwareModuleIdAndTargetVisible(final Pageable pageable, final long id) {
assertSoftwareModuleExists(id);
return JpaManagementHelper.convertPage(softwareModuleMetadataRepository.findBySoftwareModuleIdAndTargetVisible(
id, true, PageRequest.of(0, RepositoryConstants.MAX_META_DATA_COUNT)), pageable);
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public SoftwareModuleMetadata updateMetaData(final SoftwareModuleMetadataCreate c) {
public SoftwareModuleMetadata updateMetadata(final SoftwareModuleMetadataCreate c) {
final JpaSoftwareModuleMetadataCreate create = (JpaSoftwareModuleMetadataCreate) c;
final Long id = create.getSoftwareModuleId();
assertSoftwareModuleExists(id);
assertMetaDataQuota(id, 1);
assertMetadataQuota(id, 1);
// touch to update revision and last modified timestamp
JpaManagementHelper.touch(entityManager, softwareModuleRepository, (JpaSoftwareModule) get(id)
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id)));
JpaManagementHelper.touch(
entityManager, softwareModuleRepository,
(JpaSoftwareModule) get(id).orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id)));
return saveMetadata(create);
}
@@ -326,13 +331,13 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public SoftwareModuleMetadata updateMetaData(final SoftwareModuleMetadataUpdate u) {
public SoftwareModuleMetadata updateMetadata(final SoftwareModuleMetadataUpdate u) {
final GenericSoftwareModuleMetadataUpdate update = (GenericSoftwareModuleMetadataUpdate) u;
// check if exists otherwise throw entity not found exception
final JpaSoftwareModuleMetadata metadata = (JpaSoftwareModuleMetadata) findMetaDataBySoftwareModuleId(
final JpaSoftwareModuleMetadata metadata = (JpaSoftwareModuleMetadata) findMetadata(
update.getSoftwareModuleId(), update.getKey())
.orElseThrow(() -> new EntityNotFoundException(SoftwareModuleMetadata.class, update.getSoftwareModuleId(), update.getKey()));
.orElseThrow(() -> new EntityNotFoundException("SoftwareModuleMetadata", update.getSoftwareModuleId() + ":" + update.getKey()));
update.getValue().ifPresent(metadata::setValue);
update.isTargetVisible().ifPresent(metadata::setTargetVisible);
@@ -345,9 +350,9 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteMetaData(final long id, final String key) {
final JpaSoftwareModuleMetadata metadata = (JpaSoftwareModuleMetadata) findMetaDataBySoftwareModuleId0(id, key)
.orElseThrow(() -> new EntityNotFoundException(SoftwareModuleMetadata.class, id, key));
public void deleteMetadata(final long id, final String key) {
final JpaSoftwareModuleMetadata metadata = (JpaSoftwareModuleMetadata) findMetadata(id, key)
.orElseThrow(() -> new EntityNotFoundException("SoftwareModuleMetadata", id + ":" + key));
JpaManagementHelper.touch(entityManager, softwareModuleRepository, metadata.getSoftwareModule());
softwareModuleMetadataRepository.deleteById(metadata.getId());
@@ -424,45 +429,6 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
.map(SoftwareModule.class::cast);
}
@Override
public Optional<SoftwareModuleMetadata> findMetaDataBySoftwareModuleId(final long id, final String key) {
return findMetaDataBySoftwareModuleId0(id, key);
}
private Optional<SoftwareModuleMetadata> findMetaDataBySoftwareModuleId0(final long id, final String key) {
assertSoftwareModuleExists(id);
return softwareModuleMetadataRepository.findById(new SwMetadataCompositeKey(id, key))
.map(SoftwareModuleMetadata.class::cast);
}
@Override
public Page<SoftwareModuleMetadata> findMetaDataBySoftwareModuleId(final Pageable pageable, final long id) {
assertSoftwareModuleExists(id);
return JpaManagementHelper.findAllWithCountBySpec(softwareModuleMetadataRepository,
Collections.singletonList(metadataBySoftwareModuleIdSpec(id)), pageable
);
}
@Override
public Page<SoftwareModuleMetadata> findMetaDataBySoftwareModuleIdAndTargetVisible(final Pageable pageable,
final long id) {
assertSoftwareModuleExists(id);
return JpaManagementHelper.convertPage(softwareModuleMetadataRepository.findBySoftwareModuleIdAndTargetVisible(
id, true, PageRequest.of(0, RepositoryConstants.MAX_META_DATA_COUNT)), pageable);
}
@Override
public Page<SoftwareModuleMetadata> findMetaDataByRsql(final long id, final String rsqlParam, final Pageable pageable) {
assertSoftwareModuleExists(id);
final List<Specification<JpaSoftwareModuleMetadata>> specList = Arrays
.asList(RSQLUtility.buildRsqlSpecification(rsqlParam, SoftwareModuleMetadataFields.class,
virtualPropertyReplacer, database), metadataBySoftwareModuleIdSpec(id));
return JpaManagementHelper.findAllWithCountBySpec(softwareModuleMetadataRepository, specList, pageable);
}
@Override
public Slice<SoftwareModule> findByType(final long typeId, final Pageable pageable) {
assertSoftwareModuleTypeExists(typeId);
@@ -490,18 +456,6 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
return softwareModuleRepository.count(SoftwareModuleSpecification.byAssignedToDs(distributionSetId));
}
@Override
public long countMetaDataBySoftwareModuleId(final long id) {
assertSoftwareModuleExists(id);
return softwareModuleMetadataRepository.countBySoftwareModuleId(id);
}
private static Stream<JpaSoftwareModuleMetadataCreate> createJpaMetadataCreateStream(
final Collection<SoftwareModuleMetadataCreate> create) {
return create.stream().map(JpaSoftwareModuleMetadataCreate.class::cast);
}
private static Specification<JpaSoftwareModuleMetadata> metadataBySoftwareModuleIdSpec(final long id) {
return (root, query, cb) -> cb.equal(root.get(JpaSoftwareModuleMetadata_.softwareModule).get(AbstractJpaBaseEntity_.id), id);
}
@@ -526,23 +480,30 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
return softwareModuleMetadataRepository.save(create.build());
}
private void assertSoftwareModuleMetadataDoesNotExist(final Long id,
final JpaSoftwareModuleMetadataCreate md) {
private Optional<SoftwareModuleMetadata> findMetadata(final long id, final String key) {
assertSoftwareModuleExists(id);
return softwareModuleMetadataRepository.findById(new SwMetadataCompositeKey(id, key))
.map(SoftwareModuleMetadata.class::cast);
}
private void assertSoftwareModuleMetadataDoesNotExist(final Long id, final JpaSoftwareModuleMetadataCreate md) {
if (softwareModuleMetadataRepository.existsById(new SwMetadataCompositeKey(id, md.getKey()))) {
throw new EntityAlreadyExistsException("Metadata entry with key '" + md.getKey() + "' already exists!");
}
}
/**
* 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 id The software module ID.
* @param requested Number of meta data entries to be created.
* @param requested Number of meta-data entries to be created.
*/
private void assertMetaDataQuota(final Long id, final int requested) {
private void assertMetadataQuota(final Long id, final int requested) {
final int maxMetaData = quotaManagement.getMaxMetaDataEntriesPerSoftwareModule();
QuotaHelper.assertAssignmentQuota(id, requested, maxMetaData, SoftwareModuleMetadata.class,
SoftwareModule.class, softwareModuleMetadataRepository::countBySoftwareModuleId);
QuotaHelper.assertAssignmentQuota(
id, requested, maxMetaData, SoftwareModuleMetadata.class, SoftwareModule.class,
softwareModuleMetadataRepository::countBySoftwareModuleId);
}
private void assertSoftwareModuleExists(final Long id) {

View File

@@ -1,85 +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.model;
import java.io.Serial;
import java.util.Objects;
import jakarta.persistence.Basic;
import jakarta.persistence.Column;
import jakarta.persistence.Id;
import jakarta.persistence.MappedSuperclass;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import org.eclipse.hawkbit.repository.model.MetaData;
/**
* Meta data for entities.
*/
@MappedSuperclass
public abstract class AbstractJpaMetaData implements MetaData {
@Serial
private static final long serialVersionUID = 1L;
@Id
@Column(name = "meta_key", nullable = false, length = MetaData.KEY_MAX_SIZE, updatable = false)
@Size(min = 1, max = MetaData.KEY_MAX_SIZE)
@NotNull
private String key;
@Column(name = "meta_value", length = MetaData.VALUE_MAX_SIZE)
@Size(max = MetaData.VALUE_MAX_SIZE)
@Basic
private String value;
protected AbstractJpaMetaData(final String key, final String value) {
this.key = key;
this.value = value;
}
protected AbstractJpaMetaData() {
// Default constructor needed for JPA entities
}
@Override
public String getKey() {
return key;
}
public void setKey(final String key) {
this.key = key;
}
@Override
public String getValue() {
return value;
}
public void setValue(final String value) {
this.value = value;
}
@Override
public int hashCode() {
return Objects.hash(key, value);
}
@Override
public boolean equals(final Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
final AbstractJpaMetaData that = (AbstractJpaMetaData) o;
return Objects.equals(key, that.key) && Objects.equals(value, that.value);
}
}

View File

@@ -116,8 +116,8 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen
name = "sp_ds_metadata",
joinColumns = { @JoinColumn(name = "ds", nullable = false) },
foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_metadata_ds"))
@MapKeyColumn(name = "meta_key", length = DistributionSet.METADATA_KEY_SIZE)
@Column(name = "meta_value", length = DistributionSet.METADATA_VALUE_SIZE)
@MapKeyColumn(name = "meta_key", length = DistributionSet.METADATA_MAX_KEY_SIZE)
@Column(name = "meta_value", length = DistributionSet.METADATA_MAX_VALUE_SIZE)
private Map<String, String> metadata;
@Column(name = "complete")

View File

@@ -13,10 +13,13 @@ import java.io.Serial;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import jakarta.persistence.CascadeType;
import jakarta.persistence.CollectionTable;
import jakarta.persistence.Column;
import jakarta.persistence.ConstraintMode;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.ForeignKey;
@@ -24,6 +27,7 @@ import jakarta.persistence.Index;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.MapKeyColumn;
import jakarta.persistence.NamedAttributeNode;
import jakarta.persistence.NamedEntityGraph;
import jakarta.persistence.OneToMany;
@@ -69,8 +73,6 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement
@Serial
private static final long serialVersionUID = 1L;
private static final String DELETED_PROPERTY = "deleted";
@Setter
@ManyToOne
@JoinColumn(name = "sm_type", nullable = false, updatable = false,
@@ -92,11 +94,16 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement
@Column(name = "encrypted")
private boolean encrypted;
@ToString.Exclude
@OneToMany(mappedBy = "softwareModule", fetch = FetchType.LAZY,
cascade = { CascadeType.REMOVE },
targetEntity = JpaSoftwareModuleMetadata.class)
private List<JpaSoftwareModuleMetadata> metadata;
// no cascade option on an ElementCollection, the target objects are always persisted, merged, removed with their parent.
@Getter
@ElementCollection
@CollectionTable(
name = "sp_sm_metadata",
joinColumns = { @JoinColumn(name = "sm", nullable = false) },
foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_sm_metadata_sm"))
@MapKeyColumn(name = "meta_key", length = SoftwareModule.METADATA_KEY_MAX_SIZE)
@Column(name = "meta_value", length = SoftwareModule.METADATA_VALUE_MAX_SIZE)
private Map<String, String> metadata;
@Column(name = "locked")
private boolean locked;

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.jpa.model;
import java.io.Serial;
import jakarta.persistence.Basic;
import jakarta.persistence.Column;
import jakarta.persistence.ConstraintMode;
import jakarta.persistence.Entity;
@@ -21,11 +22,11 @@ import jakarta.persistence.IdClass;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
@@ -34,31 +35,42 @@ import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
*/
@NoArgsConstructor // Default constructor for JPA
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@IdClass(SwMetadataCompositeKey.class)
@Entity
@Table(name = "sp_sm_metadata")
public class JpaSoftwareModuleMetadata extends AbstractJpaMetaData implements SoftwareModuleMetadata {
public class JpaSoftwareModuleMetadata implements SoftwareModuleMetadata {
@Serial
private static final long serialVersionUID = 1L;
@Id
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "sm", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_sm_metadata_sm"))
@JoinColumn(
name = "sm", nullable = false, updatable = false,
foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_sm_metadata_sm"))
private JpaSoftwareModule softwareModule;
@Id
@Column(name = "meta_key", nullable = false, length = SoftwareModule.METADATA_KEY_MAX_SIZE, updatable = false)
@Size(min = 1, max = SoftwareModule.METADATA_KEY_MAX_SIZE)
@NotNull
private String key;
@Column(name = "meta_value", length = SoftwareModule.METADATA_VALUE_MAX_SIZE)
@Size(max = SoftwareModule.METADATA_VALUE_MAX_SIZE)
@Basic
private String value;
@Column(name = "target_visible")
private boolean targetVisible;
public JpaSoftwareModuleMetadata(final String key, final SoftwareModule softwareModule, final String value) {
super(key, value);
this.softwareModule = (JpaSoftwareModule) softwareModule;
this(key, softwareModule, value, false);
}
public JpaSoftwareModuleMetadata(final String key, final SoftwareModule softwareModule, final String value, final boolean targetVisible) {
super(key, value);
this.key = key;
this.value = value;
this.softwareModule = (JpaSoftwareModule) softwareModule;
this.targetVisible = targetVisible;
}

View File

@@ -37,7 +37,6 @@ import jakarta.persistence.MapKeyColumn;
import jakarta.persistence.NamedAttributeNode;
import jakarta.persistence.NamedEntityGraph;
import jakarta.persistence.NamedEntityGraphs;
import jakarta.persistence.NamedSubgraph;
import jakarta.persistence.OneToMany;
import jakarta.persistence.PrimaryKeyJoinColumn;
import jakarta.persistence.Table;
@@ -187,8 +186,8 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
name = "sp_target_attributes",
joinColumns = { @JoinColumn(name = "target", nullable = false) },
foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_target_attributes_target"))
@MapKeyColumn(name = "attribute_key", length = Target.CONTROLLER_ATTRIBUTE_KEY_SIZE)
@Column(name = "attribute_value", length = Target.CONTROLLER_ATTRIBUTE_VALUE_SIZE)
@MapKeyColumn(name = "attribute_key", length = Target.CONTROLLER_ATTRIBUTE_MAX_KEY_SIZE)
@Column(name = "attribute_value", length = Target.CONTROLLER_ATTRIBUTE_MAX_VALUE_SIZE)
private Map<String, String> controllerAttributes;
// no cascade option on an ElementCollection, the target objects are always persisted, merged, removed with their parent
@@ -198,8 +197,8 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
name = "sp_target_metadata",
joinColumns = { @JoinColumn(name = "target", nullable = false) },
foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_target_metadata_target"))
@MapKeyColumn(name = "meta_key", length = Target.CONTROLLER_METADATA_KEY_SIZE)
@Column(name = "meta_value", length = Target.CONTROLLER_METADATA_VALUE_SIZE)
@MapKeyColumn(name = "meta_key", length = Target.METADATA_MAX_KEY_SIZE)
@Column(name = "meta_value", length = Target.METADATA_MAX_VALUE_SIZE)
private Map<String, String> metadata;
@OneToMany(mappedBy = "target", fetch = FetchType.LAZY, cascade = { CascadeType.REMOVE }, targetEntity = JpaAction.class)

View File

@@ -46,17 +46,20 @@ class SoftwareManagementSecurityTest
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
void createMetaDataPermissionsCheck() {
assertPermissions(
() -> softwareModuleManagement.updateMetaData(entityFactory.softwareModuleMetadata().create(1L).key("key").value("value")),
() -> softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().create(1L).key("key").value("value")),
List.of(SpPermission.UPDATE_REPOSITORY));
assertPermissions(() -> softwareModuleManagement.putMetaData(
List.of(entityFactory.softwareModuleMetadata().create(1L).key("key").value("value"))), List.of(SpPermission.UPDATE_REPOSITORY));
assertPermissions(() -> {
softwareModuleManagement.createMetadata(
List.of(entityFactory.softwareModuleMetadata().create(1L).key("key").value("value")));
return null;
}, List.of(SpPermission.UPDATE_REPOSITORY));
}
@Test
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
void deleteMetaDataPermissionsCheck() {
assertPermissions(() -> {
softwareModuleManagement.deleteMetaData(1L, "key");
softwareModuleManagement.deleteMetadata(1L, "key");
return null;
}, List.of(SpPermission.UPDATE_REPOSITORY));
}
@@ -88,19 +91,13 @@ class SoftwareManagementSecurityTest
@Test
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
void getMetaDataBySoftwareModuleIdPermissionsCheck() {
assertPermissions(() -> softwareModuleManagement.findMetaDataBySoftwareModuleId(1L, "key"), List.of(SpPermission.READ_REPOSITORY));
assertPermissions(() -> softwareModuleManagement.getMetadata(1L, "key"), List.of(SpPermission.READ_REPOSITORY));
}
@Test
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
void findMetaDataBySoftwareModuleIdPermissionsCheck() {
assertPermissions(() -> softwareModuleManagement.findMetaDataBySoftwareModuleId(PAGE, 1L), List.of(SpPermission.READ_REPOSITORY));
}
@Test
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
void countMetaDataBySoftwareModuleIdPermissionsCheck() {
assertPermissions(() -> softwareModuleManagement.countMetaDataBySoftwareModuleId(1L), List.of(SpPermission.READ_REPOSITORY));
assertPermissions(() -> softwareModuleManagement.getMetadata(1L), List.of(SpPermission.READ_REPOSITORY));
}
@Test
@@ -110,12 +107,6 @@ class SoftwareManagementSecurityTest
List.of(SpPermission.READ_REPOSITORY));
}
@Test
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
void findMetaDataByRsqlPermissionsCheck() {
assertPermissions(() -> softwareModuleManagement.findMetaDataByRsql(1L, "key==value", PAGE), List.of(SpPermission.READ_REPOSITORY));
}
@Test
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
void findByTypePermissionsCheck() {
@@ -144,7 +135,7 @@ class SoftwareManagementSecurityTest
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
void updateMetaDataPermissionsCheck() {
assertPermissions(
() -> softwareModuleManagement.updateMetaData(entityFactory.softwareModuleMetadata().update(1L, "key").value("value")),
() -> softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().update(1L, "key").value("value")),
List.of(SpPermission.UPDATE_REPOSITORY));
}

View File

@@ -29,6 +29,7 @@ import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataCreate;
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent;
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.LockedException;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.jpa.RandomGeneratedInputStream;
@@ -58,6 +59,8 @@ import org.springframework.data.domain.PageRequest;
@Story("Software Module Management")
class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
private static final PageRequest PAGE_REQUEST_100 = PageRequest.of(0, 100);
@Test
@Description("Verifies that management get access reacts as specified on calls for non existing entities by means "
+ "of Optional not present.")
@@ -70,7 +73,8 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
assertThat(softwareModuleManagement.findByNameAndVersionAndType(NOT_EXIST_ID, NOT_EXIST_ID, osType.getId()))
.isNotPresent();
assertThat(softwareModuleManagement.findMetaDataBySoftwareModuleId(module.getId(), NOT_EXIST_ID)).isNotPresent();
assertThatExceptionOfType(EntityNotFoundException.class)
.isThrownBy(() -> softwareModuleManagement.getMetadata(module.getId(), NOT_EXIST_ID));
}
@Test
@@ -82,57 +86,40 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
verifyThrownExceptionBy(
() -> softwareModuleManagement.create(Collections
.singletonList(entityFactory.softwareModule().create().name("xxx").type(NOT_EXIST_ID))),
"SoftwareModuleType");
.singletonList(entityFactory.softwareModule().create().name("xxx").type(NOT_EXIST_ID))), "SoftwareModuleType");
verifyThrownExceptionBy(
() -> softwareModuleManagement
.create(entityFactory.softwareModule().create().name("xxx").type(NOT_EXIST_ID)),
"SoftwareModuleType");
.create(entityFactory.softwareModule().create().name("xxx").type(NOT_EXIST_ID)), "SoftwareModuleType");
verifyThrownExceptionBy(
() -> softwareModuleManagement.updateMetaData(
entityFactory.softwareModuleMetadata().create(NOT_EXIST_IDL).key("xxx").value("xxx")),
"SoftwareModule");
() -> softwareModuleManagement.updateMetadata(
entityFactory.softwareModuleMetadata().create(NOT_EXIST_IDL).key("xxx").value("xxx")), "SoftwareModule");
verifyThrownExceptionBy(
() -> softwareModuleManagement.putMetaData(Collections.singletonList(
entityFactory.softwareModuleMetadata().create(NOT_EXIST_IDL).key("xxx").value("xxx"))),
"SoftwareModule");
() -> softwareModuleManagement.createMetadata(Collections.singletonList(
entityFactory.softwareModuleMetadata().create(NOT_EXIST_IDL).key("xxx").value("xxx"))), "SoftwareModule");
verifyThrownExceptionBy(() -> softwareModuleManagement.delete(NOT_EXIST_IDL), "SoftwareModule");
verifyThrownExceptionBy(() -> softwareModuleManagement.delete(Collections.singletonList(NOT_EXIST_IDL)),
"SoftwareModule");
verifyThrownExceptionBy(() -> softwareModuleManagement.deleteMetaData(NOT_EXIST_IDL, "xxx"), "SoftwareModule");
verifyThrownExceptionBy(() -> softwareModuleManagement.deleteMetaData(module.getId(), NOT_EXIST_ID),
"SoftwareModuleMetadata");
verifyThrownExceptionBy(() -> softwareModuleManagement.delete(Collections.singletonList(NOT_EXIST_IDL)), "SoftwareModule");
verifyThrownExceptionBy(() -> softwareModuleManagement.deleteMetadata(NOT_EXIST_IDL, "xxx"), "SoftwareModule");
verifyThrownExceptionBy(
() -> softwareModuleManagement.updateMetaData(
entityFactory.softwareModuleMetadata().update(NOT_EXIST_IDL, "xxx").value("xxx")),
"SoftwareModule");
() -> softwareModuleManagement.updateMetadata(
entityFactory.softwareModuleMetadata().update(NOT_EXIST_IDL, "xxx").value("xxx")), "SoftwareModule");
verifyThrownExceptionBy(
() -> softwareModuleManagement.updateMetaData(
entityFactory.softwareModuleMetadata().update(module.getId(), NOT_EXIST_ID).value("xxx")),
"SoftwareModuleMetadata");
() -> softwareModuleManagement.updateMetadata(
entityFactory.softwareModuleMetadata().update(module.getId(), NOT_EXIST_ID).value("xxx")), "SoftwareModuleMetadata");
verifyThrownExceptionBy(() -> softwareModuleManagement.findByAssignedTo(NOT_EXIST_IDL, PAGE),
"DistributionSet");
verifyThrownExceptionBy(() -> softwareModuleManagement.findByAssignedTo(NOT_EXIST_IDL, PAGE), "DistributionSet");
verifyThrownExceptionBy(() -> softwareModuleManagement.findByNameAndVersionAndType("xxx", "xxx", NOT_EXIST_IDL),
"SoftwareModuleType");
verifyThrownExceptionBy(
() -> softwareModuleManagement.findMetaDataBySoftwareModuleId(NOT_EXIST_IDL, NOT_EXIST_ID),
"SoftwareModule");
verifyThrownExceptionBy(() -> softwareModuleManagement.getMetadata(NOT_EXIST_IDL, NOT_EXIST_ID), "SoftwareModule");
verifyThrownExceptionBy(() -> softwareModuleManagement.findMetaDataBySoftwareModuleId(PAGE, NOT_EXIST_IDL),
"SoftwareModule");
verifyThrownExceptionBy(() -> softwareModuleManagement.findMetaDataByRsql(NOT_EXIST_IDL, "name==*", PAGE),
"SoftwareModule");
verifyThrownExceptionBy(() -> softwareModuleManagement.getMetadata(NOT_EXIST_IDL), "SoftwareModule");
verifyThrownExceptionBy(() -> softwareModuleManagement.findByType(NOT_EXIST_IDL, PAGE), "SoftwareModule");
verifyThrownExceptionBy(
() -> softwareModuleManagement.update(entityFactory.softwareModule().update(NOT_EXIST_IDL)),
"SoftwareModule");
verifyThrownExceptionBy(() -> softwareModuleManagement.update(entityFactory.softwareModule().update(NOT_EXIST_IDL)), "SoftwareModule");
}
@Test
@@ -496,27 +483,37 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
final String knownKey2 = "myKnownKey2";
final String knownValue2 = "myKnownValue2";
final SoftwareModule ah = testdataFactory.createSoftwareModuleApp();
final SoftwareModule softwareModule = testdataFactory.createSoftwareModuleApp();
assertThat(ah.getOptLockRevision()).isEqualTo(1);
assertThat(softwareModule.getOptLockRevision()).isEqualTo(1);
final SoftwareModuleMetadataCreate swMetadata1 = entityFactory.softwareModuleMetadata().create(ah.getId())
.key(knownKey1).value(knownValue1);
final SoftwareModuleMetadataCreate swMetadata1 = entityFactory.softwareModuleMetadata()
.create(softwareModule.getId())
.key(knownKey1)
.value(knownValue1)
.targetVisible(true);
final SoftwareModuleMetadataCreate swMetadata2 = entityFactory.softwareModuleMetadata().create(ah.getId())
.key(knownKey2).value(knownValue2);
final SoftwareModuleMetadataCreate swMetadata2 = entityFactory.softwareModuleMetadata()
.create(softwareModule.getId())
.key(knownKey2)
.value(knownValue2);
final List<SoftwareModuleMetadata> softwareModuleMetadata = softwareModuleManagement
.putMetaData(Arrays.asList(swMetadata1, swMetadata2));
softwareModuleManagement.createMetadata(Arrays.asList(swMetadata1, swMetadata2));
final SoftwareModule changedLockRevisionModule = softwareModuleManagement.get(ah.getId()).get();
final SoftwareModule changedLockRevisionModule = softwareModuleManagement.get(softwareModule.getId()).get();
assertThat(changedLockRevisionModule.getOptLockRevision()).isEqualTo(2);
assertThat(softwareModuleMetadata).hasSize(2);
assertThat(softwareModuleMetadata.get(0)).isNotNull();
assertThat(softwareModuleMetadata.get(0).getValue()).isEqualTo(knownValue1);
assertThat(((JpaSoftwareModuleMetadata) softwareModuleMetadata.get(0)).getId().getKey()).isEqualTo(knownKey1);
assertThat(softwareModuleMetadata.get(0).getEntityId()).isEqualTo(ah.getId());
assertThat(softwareModuleManagement.getMetadata(softwareModule.getId(), knownKey1)).satisfies(metadata -> {
assertThat(metadata.getKey()).isEqualTo(knownKey1);
assertThat(metadata.getValue()).isEqualTo(knownValue1);
assertThat(metadata.isTargetVisible()).isTrue();
});
assertThat(softwareModuleManagement.getMetadata(softwareModule.getId(), knownKey2)).satisfies(metadata -> {
assertThat(metadata.getKey()).isEqualTo(knownKey2);
assertThat(metadata.getValue()).isEqualTo(knownValue2);
assertThat(metadata.isTargetVisible()).isFalse();
});
}
@Test
@@ -527,7 +524,7 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
final SoftwareModule module = testdataFactory.createSoftwareModuleApp("m1");
final int maxMetaData = quotaManagement.getMaxMetaDataEntriesPerSoftwareModule();
for (int i = 0; i < maxMetaData; ++i) {
softwareModuleManagement.updateMetaData(
softwareModuleManagement.updateMetadata(
entityFactory.softwareModuleMetadata().create(module.getId()).key("k" + i).value("v" + i));
}
@@ -535,7 +532,7 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
final SoftwareModuleMetadataCreate metadata = entityFactory.softwareModuleMetadata().create(module.getId())
.key("k" + maxMetaData).value("v" + maxMetaData);
assertThatExceptionOfType(AssignmentQuotaExceededException.class)
.isThrownBy(() -> softwareModuleManagement.updateMetaData(metadata));
.isThrownBy(() -> softwareModuleManagement.updateMetadata(metadata));
// add multiple meta data entries at once
final SoftwareModule module2 = testdataFactory.createSoftwareModuleApp("m2");
@@ -545,13 +542,13 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
}
// quota exceeded
assertThatExceptionOfType(AssignmentQuotaExceededException.class)
.isThrownBy(() -> softwareModuleManagement.putMetaData(create));
.isThrownBy(() -> softwareModuleManagement.createMetadata(create));
// add some meta data entries
final SoftwareModule module3 = testdataFactory.createSoftwareModuleApp("m3");
final int firstHalf = Math.round((maxMetaData) / 2.f);
for (int i = 0; i < firstHalf; ++i) {
softwareModuleManagement.updateMetaData(
softwareModuleManagement.updateMetadata(
entityFactory.softwareModuleMetadata().create(module3.getId()).key("k" + i).value("v" + i));
}
// add too many data entries
@@ -562,7 +559,7 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
}
// quota exceeded
assertThatExceptionOfType(AssignmentQuotaExceededException.class)
.isThrownBy(() -> softwareModuleManagement.putMetaData(create2));
.isThrownBy(() -> softwareModuleManagement.createMetadata(create2));
}
@@ -576,21 +573,21 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
final SoftwareModuleMetadataCreate metadata = entityFactory.softwareModuleMetadata()
.create(ah.getId()).key(knownKey1).value(knownValue1).targetVisible(true);
softwareModuleManagement.updateMetaData(metadata);
softwareModuleManagement.updateMetadata(metadata);
assertThatExceptionOfType(EntityAlreadyExistsException.class)
.isThrownBy(() -> softwareModuleManagement.updateMetaData(metadata))
.isThrownBy(() -> softwareModuleManagement.updateMetadata(metadata))
.withMessageContaining("Metadata").withMessageContaining(knownKey1);
final String knownKey2 = "myKnownKey2";
softwareModuleManagement.updateMetaData(entityFactory.softwareModuleMetadata().create(ah.getId()).key(knownKey2)
softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().create(ah.getId()).key(knownKey2)
.value(knownValue1).targetVisible(false));
final SoftwareModuleMetadataCreate metadata2 = entityFactory.softwareModuleMetadata().create(ah.getId())
.key(knownKey2).value(knownValue1).targetVisible(true);
assertThatExceptionOfType(EntityAlreadyExistsException.class)
.isThrownBy(() -> softwareModuleManagement.updateMetaData(metadata2))
.isThrownBy(() -> softwareModuleManagement.updateMetadata(metadata2))
.withMessageContaining("Metadata").withMessageContaining(knownKey2);
}
@@ -608,7 +605,7 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
assertThat(ah.getOptLockRevision()).isEqualTo(1);
// create an software module meta data entry
final SoftwareModuleMetadata softwareModuleMetadata = softwareModuleManagement.updateMetaData(
final SoftwareModuleMetadata softwareModuleMetadata = softwareModuleManagement.updateMetadata(
entityFactory.softwareModuleMetadata().create(ah.getId()).key(knownKey).value(knownValue));
assertThat(softwareModuleMetadata.isTargetVisible()).isFalse();
assertThat(softwareModuleMetadata.getValue()).isEqualTo(knownValue);
@@ -619,7 +616,7 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
assertThat(changedLockRevisionModule.getOptLockRevision()).isEqualTo(2);
// update the software module metadata
final SoftwareModuleMetadata updated = softwareModuleManagement.updateMetaData(entityFactory
final SoftwareModuleMetadata updated = softwareModuleManagement.updateMetadata(entityFactory
.softwareModuleMetadata().update(ah.getId(), knownKey).value(knownUpdateValue).targetVisible(true));
// we are updating the sw metadata so also modifying the base software
@@ -632,7 +629,7 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
assertThat(updated.getValue()).isEqualTo(knownUpdateValue);
assertThat(updated.isTargetVisible()).isTrue();
assertThat(((JpaSoftwareModuleMetadata) updated).getId().getKey()).isEqualTo(knownKey);
assertThat(updated.getEntityId()).isEqualTo(ah.getId());
assertThat(((JpaSoftwareModuleMetadata) updated).getSoftwareModule().getId()).isEqualTo(ah.getId());
}
@Test
@@ -643,19 +640,18 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
final SoftwareModule swModule = testdataFactory.createSoftwareModuleApp();
softwareModuleManagement.updateMetaData(
softwareModuleManagement.updateMetadata(
entityFactory.softwareModuleMetadata().create(swModule.getId()).key(knownKey1).value(knownValue1));
assertThat(softwareModuleManagement.findMetaDataBySoftwareModuleId(PageRequest.of(0, 10), swModule.getId())
.getContent()).as("Contains the created metadata element").allSatisfy(metadata -> {
assertThat(metadata.getSoftwareModule().getId()).isEqualTo(swModule.getId());
assertThat(metadata.getKey()).isEqualTo(knownKey1);
assertThat(metadata.getValue()).isEqualTo(knownValue1);
});
assertThat(softwareModuleManagement.getMetadata(swModule.getId()))
.as("Contains the created metadata element").allSatisfy(metadata -> {
assertThat(((JpaSoftwareModuleMetadata) metadata).getSoftwareModule().getId()).isEqualTo(swModule.getId());
assertThat(metadata.getKey()).isEqualTo(knownKey1);
assertThat(metadata.getValue()).isEqualTo(knownValue1);
});
softwareModuleManagement.deleteMetaData(swModule.getId(), knownKey1);
assertThat(softwareModuleManagement.findMetaDataBySoftwareModuleId(PageRequest.of(0, 10), swModule.getId())
.getContent()).as("Metadata elements are").isEmpty();
softwareModuleManagement.deleteMetadata(swModule.getId(), knownKey1);
assertThat(softwareModuleManagement.getMetadata(swModule.getId())).as("Metadata elements are").isEmpty();
}
@Test
@@ -666,16 +662,16 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
final SoftwareModule ah = testdataFactory.createSoftwareModuleApp();
softwareModuleManagement.updateMetaData(
softwareModuleManagement.updateMetadata(
entityFactory.softwareModuleMetadata().create(ah.getId()).key(knownKey1).value(knownValue1));
assertThat(softwareModuleManagement.findMetaDataBySoftwareModuleId(ah.getId(), "doesnotexist")).isNotPresent();
assertThatExceptionOfType(EntityNotFoundException.class)
.isThrownBy(() -> softwareModuleManagement.getMetadata(ah.getId(), "doesnotexist"));
}
@Test
@Description("Queries and loads the metadata related to a given software module.")
void findAllSoftwareModuleMetadataBySwId() {
final SoftwareModule sw1 = testdataFactory.createSoftwareModuleApp();
final int metadataCountSw1 = 8;
@@ -683,38 +679,28 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
final int metadataCountSw2 = 10;
for (int index = 0; index < metadataCountSw1; index++) {
softwareModuleManagement.updateMetaData(entityFactory.softwareModuleMetadata().create(sw1.getId())
softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().create(sw1.getId())
.key("key" + index).value("value" + index).targetVisible(true));
}
for (int index = 0; index < metadataCountSw2; index++) {
softwareModuleManagement.updateMetaData(entityFactory.softwareModuleMetadata().create(sw2.getId())
softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().create(sw2.getId())
.key("key" + index).value("value" + index).targetVisible(false));
}
Page<SoftwareModuleMetadata> metadataSw1 = softwareModuleManagement
.findMetaDataBySoftwareModuleId(PageRequest.of(0, 100), sw1.getId());
final List<SoftwareModuleMetadata> metadataSw1 = softwareModuleManagement.getMetadata(sw1.getId());
final List<SoftwareModuleMetadata> metadataSw2 = softwareModuleManagement.getMetadata(sw2.getId());
assertThat(metadataSw1).hasSize(metadataCountSw1);
assertThat(metadataSw2).hasSize(metadataCountSw2);
Page<SoftwareModuleMetadata> metadataSw2 = softwareModuleManagement
.findMetaDataBySoftwareModuleId(PageRequest.of(0, 100), sw2.getId());
assertThat(metadataSw1.getNumberOfElements()).isEqualTo(metadataCountSw1);
assertThat(metadataSw1.getTotalElements()).isEqualTo(metadataCountSw1);
assertThat(metadataSw2.getNumberOfElements()).isEqualTo(metadataCountSw2);
assertThat(metadataSw2.getTotalElements()).isEqualTo(metadataCountSw2);
metadataSw1 = softwareModuleManagement.findMetaDataBySoftwareModuleIdAndTargetVisible(PageRequest.of(0, 100),
sw1.getId());
metadataSw2 = softwareModuleManagement.findMetaDataBySoftwareModuleIdAndTargetVisible(PageRequest.of(0, 100),
sw2.getId());
assertThat(metadataSw1.getNumberOfElements()).isEqualTo(metadataCountSw1);
assertThat(metadataSw1.getTotalElements()).isEqualTo(metadataCountSw1);
assertThat(metadataSw2.getNumberOfElements()).isZero();
assertThat(metadataSw2.getTotalElements()).isZero();
final Page<SoftwareModuleMetadata> metadataSw1V = softwareModuleManagement.findMetaDataBySoftwareModuleIdAndTargetVisible(
PAGE_REQUEST_100, sw1.getId());
final Page<SoftwareModuleMetadata> metadataSw2V = softwareModuleManagement.findMetaDataBySoftwareModuleIdAndTargetVisible(
PAGE_REQUEST_100, sw2.getId());
assertThat(metadataSw1V.getNumberOfElements()).isEqualTo(metadataCountSw1);
assertThat(metadataSw1V.getTotalElements()).isEqualTo(metadataCountSw1);
assertThat(metadataSw2V.getNumberOfElements()).isZero();
assertThat(metadataSw2V.getTotalElements()).isZero();
}
@Test
@@ -757,7 +743,8 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
.isTrue();
// try add
final ArtifactUpload artifactUpload = new ArtifactUpload(new ByteArrayInputStream(new byte[] { 2 }), softwareModuleId, "artifact2", false, 1);
final ArtifactUpload artifactUpload = new ArtifactUpload(new ByteArrayInputStream(new byte[] { 2 }), softwareModuleId, "artifact2",
false, 1);
assertThatExceptionOfType(LockedException.class)
.as("Attempt to modify a locked SM artifacts should throw an exception")
.isThrownBy(() -> artifactManagement.create(artifactUpload));

View File

@@ -51,11 +51,11 @@ class RSQLSoftwareModuleFieldTest extends AbstractJpaIntegrationTest {
final SoftwareModuleMetadataCreate softwareModuleMetadata = entityFactory.softwareModuleMetadata()
.create(ah.getId()).key("metaKey").value("metaValue");
softwareModuleManagement.updateMetaData(softwareModuleMetadata);
softwareModuleManagement.updateMetadata(softwareModuleMetadata);
final SoftwareModuleMetadataCreate softwareModuleMetadata2 = entityFactory.softwareModuleMetadata()
.create(ah2.getId()).key("metaKey").value("value");
softwareModuleManagement.updateMetaData(softwareModuleMetadata2);
softwareModuleManagement.updateMetadata(softwareModuleMetadata2);
}
@Test

View File

@@ -1,95 +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.rsql;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.ArrayList;
import java.util.List;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.qameta.allure.Story;
import org.eclipse.hawkbit.repository.SoftwareModuleMetadataFields;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataCreate;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
import org.eclipse.hawkbit.repository.test.util.TestdataFactory;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
@Feature("Component Tests - Repository")
@Story("RSQL filter software module metadata")
class RSQLSoftwareModuleMetadataFieldsTest extends AbstractJpaIntegrationTest {
private Long softwareModuleId;
@BeforeEach
void setupBeforeTest() {
final SoftwareModule softwareModule = testdataFactory.createSoftwareModule(TestdataFactory.SM_TYPE_APP);
softwareModuleId = softwareModule.getId();
final List<SoftwareModuleMetadataCreate> metadata = new ArrayList<>(5);
for (int i = 0; i < 5; i++) {
metadata.add(
entityFactory.softwareModuleMetadata().create(softwareModule.getId()).key("" + i).value("" + i));
}
metadata.add(entityFactory.softwareModuleMetadata().create(softwareModule.getId()).key("targetVisible")
.value("targetVisible").targetVisible(true));
metadata.add(entityFactory.softwareModuleMetadata().create(softwareModule.getId()).key("emptyMd")
.targetVisible(true));
softwareModuleManagement.putMetaData(metadata);
}
@Test
@Description("Test filter software module metadata by key")
void testFilterByParameterKey() {
assertRSQLQuery(SoftwareModuleMetadataFields.KEY.name() + "==1", 1);
assertRSQLQuery(SoftwareModuleMetadataFields.KEY.name() + "!=1", 6);
assertRSQLQuery(SoftwareModuleMetadataFields.KEY.name() + "=in=(1,2)", 2);
assertRSQLQuery(SoftwareModuleMetadataFields.KEY.name() + "=out=(1,2)", 5);
}
@Test
@Description("Test fitler software module metadata by value")
void testFilterByParameterValue() {
assertRSQLQuery(SoftwareModuleMetadataFields.VALUE.name() + "==''", 1);
assertRSQLQuery(SoftwareModuleMetadataFields.VALUE.name() + "!=''", 6);
assertRSQLQuery(SoftwareModuleMetadataFields.VALUE.name() + "==1", 1);
assertRSQLQuery(SoftwareModuleMetadataFields.VALUE.name() + "!=1", 6);
assertRSQLQuery(SoftwareModuleMetadataFields.VALUE.name() + "=in=(1,2)", 2);
assertRSQLQuery(SoftwareModuleMetadataFields.VALUE.name() + "=out=(1,2)", 5);
}
@Test
@Description("Test fitler software module metadata by target visible")
void testFilterByParameterTargetVisible() {
assertRSQLQuery(SoftwareModuleMetadataFields.TARGETVISIBLE.name() + "==true", 2);
assertRSQLQuery(SoftwareModuleMetadataFields.TARGETVISIBLE.name() + "==false", 5);
assertRSQLQuery(SoftwareModuleMetadataFields.TARGETVISIBLE.name() + "!=false", 2);
assertRSQLQuery(SoftwareModuleMetadataFields.TARGETVISIBLE.name() + "!=true", 5);
}
private void assertRSQLQuery(final String rsqlParam, final long expectedEntities) {
final Page<SoftwareModuleMetadata> findEnitity = softwareModuleManagement
.findMetaDataByRsql(softwareModuleId, rsqlParam, PageRequest.of(0, 100));
final long countAllEntities = findEnitity.getTotalElements();
assertThat(findEnitity).isNotNull();
assertThat(countAllEntities).isEqualTo(expectedEntities);
}
}

View File

@@ -21,7 +21,6 @@ import java.time.ZonedDateTime;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import lombok.extern.slf4j.Slf4j;
@@ -58,7 +57,6 @@ import org.eclipse.hawkbit.repository.model.DeploymentRequest;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.MetaData;
import org.eclipse.hawkbit.repository.model.RepositoryModelConstants;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.repository.model.Target;

View File

@@ -23,10 +23,10 @@ public class TargetTestData {
static {
final Random rand = new SecureRandom();
ATTRIBUTE_KEY_TOO_LONG = generateRandomStringWithLength(Target.CONTROLLER_ATTRIBUTE_KEY_SIZE + 1, rand);
ATTRIBUTE_KEY_VALID = generateRandomStringWithLength(Target.CONTROLLER_ATTRIBUTE_KEY_SIZE, rand);
ATTRIBUTE_VALUE_TOO_LONG = generateRandomStringWithLength(Target.CONTROLLER_ATTRIBUTE_VALUE_SIZE + 1, rand);
ATTRIBUTE_VALUE_VALID = generateRandomStringWithLength(Target.CONTROLLER_ATTRIBUTE_VALUE_SIZE, rand);
ATTRIBUTE_KEY_TOO_LONG = generateRandomStringWithLength(Target.CONTROLLER_ATTRIBUTE_MAX_KEY_SIZE + 1, rand);
ATTRIBUTE_KEY_VALID = generateRandomStringWithLength(Target.CONTROLLER_ATTRIBUTE_MAX_KEY_SIZE, rand);
ATTRIBUTE_VALUE_TOO_LONG = generateRandomStringWithLength(Target.CONTROLLER_ATTRIBUTE_MAX_VALUE_SIZE + 1, rand);
ATTRIBUTE_VALUE_VALID = generateRandomStringWithLength(Target.CONTROLLER_ATTRIBUTE_MAX_VALUE_SIZE, rand);
}
private TargetTestData() {

View File

@@ -1210,9 +1210,9 @@ public class TestdataFactory {
}
private void addTestModuleMetadata(final SoftwareModule module) {
softwareModuleManagement.updateMetaData(entityFactory.softwareModuleMetadata().create(module.getId())
softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().create(module.getId())
.key(VISIBLE_SM_MD_KEY).value(VISIBLE_SM_MD_VALUE).targetVisible(true));
softwareModuleManagement.updateMetaData(entityFactory.softwareModuleMetadata().create(module.getId())
softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().create(module.getId())
.key(INVISIBLE_SM_MD_KEY).value(INVISIBLE_SM_MD_VALUE).targetVisible(false));
}