JpaDistributionSet#metadata made Map (#2411)

Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2025-05-21 13:45:18 +03:00
committed by GitHub
parent ceba4f5cfb
commit 452d8618d7
26 changed files with 382 additions and 1216 deletions

View File

@@ -1,33 +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 DistributionSetMetadata.
*/
@Getter
public enum DistributionSetMetadataFields implements RsqlQueryField {
KEY("key"),
VALUE("value");
private final String jpaEntityFieldName;
DistributionSetMetadataFields(final String jpaEntityFieldName) {
this.jpaEntityFieldName = jpaEntityFieldName;
}
@Override
public String identifierFieldName() {
return KEY.getJpaEntityFieldName();
}
}

View File

@@ -35,6 +35,7 @@ import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTarget;
import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQuery;
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;
@@ -44,6 +45,7 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
/**
* REST Resource handling for DistributionSet CRUD operations.
@@ -473,15 +475,50 @@ public interface MgmtDistributionSetRestApi {
@RequestBody List<MgmtTargetAssignmentRequestBody> assignments,
@RequestParam(value = "offline", required = false) Boolean offline);
/**
* Creates a list of meta-data for a specific distribution set.
*
* @param distributionSetId the ID of the distribution set to create meta data for
* @param metadataRest the list of meta-data entries to create
*/
@Operation(summary = "Create a list of meta data for a specific distribution set",
description = "Create a list of meta data entries Required permissions: READ_REPOSITORY and UPDATE_TARGET")
@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 = "Distribution Set 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.DISTRIBUTIONSET_V1_REQUEST_MAPPING + "/{distributionSetId}/metadata",
consumes = { MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE })
@ResponseStatus(HttpStatus.CREATED)
void createMetadata(
@PathVariable("distributionSetId") Long distributionSetId,
@RequestBody List<MgmtMetadata> metadataRest);
/**
* Gets a paged list of meta-data for a distribution set.
*
* @param distributionSetId the ID of the distribution set for the meta-data
* @param pagingOffsetParam the offset of list of targets 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 meta data
*/
@Operation(summary = "Return meta data for Distribution Set", description = "Get a paged list of meta data for a " +
@@ -507,37 +544,14 @@ public interface MgmtDistributionSetRestApi {
})
@GetMapping(value = MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING + "/{distributionSetId}/metadata",
produces = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<PagedList<MgmtMetadata>> getMetadata(
@PathVariable("distributionSetId") Long distributionSetId,
@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<MgmtMetadata>> getMetadata(@PathVariable("distributionSetId") Long distributionSetId);
/**
* Gets a single meta data value for a specific key of a distribution set.
* Gets a single meta-data value for a specific key of a distribution set.
*
* @param distributionSetId the ID of the distribution set to get the meta data from
* @param metadataKey the key of the meta data entry to retrieve the value from
* @return status OK if get request is successful with the value of the meta data
* @param distributionSetId the ID of the distribution set to get the meta-data from
* @param metadataKey the key of the meta-data entry to retrieve the value from
* @return status OK with the value of the meta-data, if the request is successful
*/
@Operation(summary = "Return single meta data value for a specific key of a Distribution Set",
description = "Get a single meta data value for a meta data key. Required permission: READ_REPOSITORY")
@@ -567,12 +581,11 @@ public interface MgmtDistributionSetRestApi {
@PathVariable("metadataKey") String metadataKey);
/**
* Updates a single meta data value of a distribution set.
* Updates a single meta-data value of a distribution set.
*
* @param distributionSetId the ID of the distribution set to update the meta data entry
* @param metadataKey the key of the meta data to update the value
* @param metadataKey the key of the meta-data to update the value
* @param metadata update body
* @return status OK if the update request is successful and the updated meta data result
*/
@Operation(summary = "Update single meta data value of a distribution set", description = "Update a single meta " +
"data value for speficic key. Required permission: UPDATE_REPOSITORY")
@@ -601,9 +614,9 @@ public interface MgmtDistributionSetRestApi {
"and the client has to wait another second.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true)))
})
@PutMapping(value = MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING + "/{distributionSetId}/metadata/{metadataKey}",
produces = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<MgmtMetadata> updateMetadata(
@PutMapping(value = MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING + "/{distributionSetId}/metadata/{metadataKey}")
@ResponseStatus(HttpStatus.OK)
void updateMetadata(
@PathVariable("distributionSetId") Long distributionSetId,
@PathVariable("metadataKey") String metadataKey,
@RequestBody MgmtMetadataBodyPut metadata);
@@ -613,7 +626,6 @@ public interface MgmtDistributionSetRestApi {
*
* @param distributionSetId the ID of the distribution set to delete the meta data entry
* @param metadataKey the key of the meta data to delete
* @return status OK if the delete request is successful
*/
@Operation(summary = "Delete a single meta data entry from the distribution set", description = "Delete a single " +
"meta data. Required permission: UPDATE_REPOSITORY")
@@ -637,51 +649,11 @@ public interface MgmtDistributionSetRestApi {
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true)))
})
@DeleteMapping(value = MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING + "/{distributionSetId}/metadata/{metadataKey}")
ResponseEntity<Void> deleteMetadata(
@ResponseStatus(HttpStatus.OK)
void deleteMetadata(
@PathVariable("distributionSetId") Long distributionSetId,
@PathVariable("metadataKey") String metadataKey);
/**
* Creates a list of meta data for a specific distribution set.
*
* @param distributionSetId the ID of the distribution set to create meta data for
* @param metadataRest the list of meta data entries to create
* @return status created if post request is successful with the value of the created meta data
*/
@Operation(summary = "Create a list of meta data for a specific distribution set", description = "Create a list " +
"of meta data entries Required permissions: READ_REPOSITORY and UPDATE_TARGET")
@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 = "Distribution Set 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.DISTRIBUTIONSET_V1_REQUEST_MAPPING + "/{distributionSetId}/metadata",
consumes = { MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE },
produces = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<List<MgmtMetadata>> createMetadata(
@PathVariable("distributionSetId") Long distributionSetId,
@RequestBody List<MgmtMetadata> metadataRest);
/**
* Assigns a list of software modules to a distribution set.
*

View File

@@ -16,6 +16,8 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
@@ -31,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.DistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.MetaData;
import org.eclipse.hawkbit.rest.json.model.ResponseList;
@@ -53,16 +54,6 @@ public final class MgmtDistributionSetMapper {
return sets.stream().map(dsRest -> fromRequest(dsRest, entityFactory)).toList();
}
static List<MetaData> fromRequestDsMetadata(final List<MgmtMetadata> metadata, final EntityFactory entityFactory) {
if (metadata == null) {
return Collections.emptyList();
}
return metadata.stream()
.map(metadataRest -> entityFactory.generateDsMetadata(metadataRest.getKey(), metadataRest.getValue()))
.toList();
}
static MgmtDistributionSet toResponse(final DistributionSet distributionSet) {
if (distributionSet == null) {
return null;
@@ -100,10 +91,8 @@ public final class MgmtDistributionSetMapper {
response.add(linkTo(methodOn(MgmtDistributionSetTypeRestApi.class)
.getDistributionSetType(distributionSet.getType().getId())).withRel("type").expand());
response.add(linkTo(methodOn(MgmtDistributionSetRestApi.class).getMetadata(response.getId(),
MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET_VALUE,
MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT_VALUE, null, null)).withRel("metadata")
.expand());
response.add(linkTo(methodOn(MgmtDistributionSetRestApi.class).getMetadata(response.getId()))
.withRel("metadata").expand());
}
static MgmtTargetAssignmentResponseBody toResponse(final DistributionSetAssignmentResult dsAssignmentResult) {
@@ -138,19 +127,21 @@ public final class MgmtDistributionSetMapper {
sets.stream().map(MgmtDistributionSetMapper::toResponse).toList());
}
static MgmtMetadata toResponseDsMetadata(final DistributionSetMetadata metadata) {
static MgmtMetadata toResponseDsMetadata(final String key, String value) {
final MgmtMetadata metadataRest = new MgmtMetadata();
metadataRest.setKey(metadata.getKey());
metadataRest.setValue(metadata.getValue());
metadataRest.setKey(key);
metadataRest.setValue(value);
return metadataRest;
}
static List<MgmtMetadata> toResponseDsMetadata(final List<DistributionSetMetadata> metadata) {
final List<MgmtMetadata> mappedList = new ArrayList<>(metadata.size());
for (final DistributionSetMetadata distributionSetMetadata : metadata) {
mappedList.add(toResponseDsMetadata(distributionSetMetadata));
}
return mappedList;
static Map<String, String> fromRequestDsMetadata(final List<MgmtMetadata> metadata) {
return metadata == null
? Collections.emptyMap()
: metadata.stream().collect(Collectors.toMap(MgmtMetadata::getKey, MgmtMetadata::getValue));
}
static List<MgmtMetadata> toResponseDsMetadata(final Map<String, String> metadata) {
return metadata.entrySet().stream().map(e -> toResponseDsMetadata(e.getKey(), e.getValue())).toList();
}
static List<MgmtDistributionSet> toResponseFromDsList(final List<DistributionSet> sets) {

View File

@@ -14,6 +14,7 @@ import java.util.AbstractMap.SimpleEntry;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.stream.Collectors;
@@ -54,7 +55,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.DistributionSetInvalidation;
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target;
@@ -279,64 +279,37 @@ public class MgmtDistributionSetResource implements MgmtDistributionSetRestApi {
}
@Override
public ResponseEntity<PagedList<MgmtMetadata>> getMetadata(
final Long distributionSetId,
final int pagingOffsetParam, final int pagingLimitParam, final String sortParam, final String rsqlParam) {
final int sanitizedOffsetParam = PagingUtility.sanitizeOffsetParam(pagingOffsetParam);
final int sanitizedLimitParam = PagingUtility.sanitizePageLimitParam(pagingLimitParam);
final Sort sorting = PagingUtility.sanitizeDistributionSetMetadataSortParam(sortParam);
public void createMetadata(final Long distributionSetId, final List<MgmtMetadata> metadataRest) {
distributionSetManagement.createMetadata(distributionSetId, MgmtDistributionSetMapper.fromRequestDsMetadata(metadataRest));
}
final Pageable pageable = new OffsetBasedPageRequest(sanitizedOffsetParam, sanitizedLimitParam, sorting);
final Page<DistributionSetMetadata> metaDataPage;
if (rsqlParam != null) {
metaDataPage = distributionSetManagement.findMetaDataByDistributionSetIdAndRsql(distributionSetId, rsqlParam, pageable
);
} else {
metaDataPage = distributionSetManagement.findMetaDataByDistributionSetId(distributionSetId, pageable);
}
return ResponseEntity
.ok(new PagedList<>(MgmtDistributionSetMapper.toResponseDsMetadata(metaDataPage.getContent()), metaDataPage.getTotalElements()));
@Override
public ResponseEntity<PagedList<MgmtMetadata>> getMetadata(final Long distributionSetId) {
final Map<String, String> metadata = distributionSetManagement.getMetadata(distributionSetId);
return ResponseEntity.ok(new PagedList<>(MgmtDistributionSetMapper.toResponseDsMetadata(metadata), metadata.size()));
}
@Override
public ResponseEntity<MgmtMetadata> getMetadataValue(final Long distributionSetId, final String metadataKey) {
// check if distribution set exists otherwise throw exception immediately
final DistributionSetMetadata findOne = distributionSetManagement
.findMetaDataByDistributionSetId(distributionSetId, metadataKey)
.orElseThrow(() -> new EntityNotFoundException(DistributionSetMetadata.class, distributionSetId, metadataKey));
return ResponseEntity.ok(MgmtDistributionSetMapper.toResponseDsMetadata(findOne));
final String metadataValue = distributionSetManagement.getMetadata(distributionSetId).get(metadataKey);
if (metadataValue == null) {
throw new EntityNotFoundException("Target metadata", distributionSetId + ":" + metadataKey);
}
return ResponseEntity.ok(MgmtDistributionSetMapper.toResponseDsMetadata(metadataKey, metadataValue));
}
@Override
public ResponseEntity<MgmtMetadata> updateMetadata(
final Long distributionSetId, final String metadataKey, final MgmtMetadataBodyPut metadata) {
// check if distribution set exists otherwise throw exception immediately
final DistributionSetMetadata updated = distributionSetManagement.updateMetaData(distributionSetId,
entityFactory.generateDsMetadata(metadataKey, metadata.getValue()));
return ResponseEntity.ok(MgmtDistributionSetMapper.toResponseDsMetadata(updated));
public void updateMetadata(final Long distributionSetId, final String metadataKey, final MgmtMetadataBodyPut metadata) {
distributionSetManagement.updateMetadata(distributionSetId, metadataKey, metadata.getValue());
}
@Override
public ResponseEntity<Void> deleteMetadata(final Long distributionSetId, final String metadataKey) {
// check if distribution set exists otherwise throw exception immediately
distributionSetManagement.deleteMetaData(distributionSetId, metadataKey);
return ResponseEntity.ok().build();
public void deleteMetadata(final Long distributionSetId, final String metadataKey) {
distributionSetManagement.deleteMetadata(distributionSetId, metadataKey);
}
@Override
public ResponseEntity<List<MgmtMetadata>> createMetadata(final Long distributionSetId, final List<MgmtMetadata> metadataRest) {
// check if distribution set exists otherwise throw exception immediately
final List<DistributionSetMetadata> created = distributionSetManagement.putMetaData(distributionSetId,
MgmtDistributionSetMapper.fromRequestDsMetadata(metadataRest, entityFactory));
return new ResponseEntity<>(MgmtDistributionSetMapper.toResponseDsMetadata(created), HttpStatus.CREATED);
}
@Override
public ResponseEntity<Void> assignSoftwareModules(
final Long distributionSetId, final List<MgmtSoftwareModuleAssignment> softwareModuleIDs) {
public ResponseEntity<Void> assignSoftwareModules(final Long distributionSetId, final List<MgmtSoftwareModuleAssignment> softwareModuleIDs) {
distributionSetManagement.assignSoftwareModules(
distributionSetId,
softwareModuleIDs.stream()

View File

@@ -15,7 +15,6 @@ import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.repository.ActionFields;
import org.eclipse.hawkbit.repository.ActionStatusFields;
import org.eclipse.hawkbit.repository.DistributionSetFields;
import org.eclipse.hawkbit.repository.DistributionSetMetadataFields;
import org.eclipse.hawkbit.repository.DistributionSetTypeFields;
import org.eclipse.hawkbit.repository.RolloutFields;
import org.eclipse.hawkbit.repository.RolloutGroupFields;
@@ -133,12 +132,12 @@ public final class PagingUtility {
return Sort.by(SortUtility.parse(ActionStatusFields.class, sortParam));
}
public static Sort sanitizeDistributionSetMetadataSortParam(final String sortParam) {
public static Sort sanitizeMetadataSortParam(final String sortParam) {
if (sortParam == null) {
// default
return Sort.by(Direction.ASC, DistributionSetMetadataFields.KEY.getJpaEntityFieldName());
return Sort.by(Direction.ASC, SoftwareModuleMetadataFields.KEY.getJpaEntityFieldName());
}
return Sort.by(SortUtility.parse(DistributionSetMetadataFields.class, sortParam));
return Sort.by(SortUtility.parse(SoftwareModuleMetadataFields.class, sortParam));
}
public static Sort sanitizeSoftwareModuleMetadataSortParam(final String sortParam) {

View File

@@ -30,6 +30,7 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.IntStream;
@@ -51,7 +52,6 @@ import org.eclipse.hawkbit.repository.jpa.specifications.ActionSpecifications;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.NamedEntity;
import org.eclipse.hawkbit.repository.model.Rollout;
@@ -890,20 +890,20 @@ class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegrationTe
.getWithDetails(distributionSetManagement.findByRsql("name==three", PAGE).getContent().get(0).getId())
.get();
assertThat((Object)JsonPath.compile("[0]_links.self.href").read(mvcResult.getResponse().getContentAsString()))
assertThat((Object) JsonPath.compile("[0]_links.self.href").read(mvcResult.getResponse().getContentAsString()))
.hasToString("http://localhost/rest/v1/distributionsets/" + one.getId());
assertThat((Object)JsonPath.compile("[0]id").read(mvcResult.getResponse().getContentAsString()))
assertThat((Object) JsonPath.compile("[0]id").read(mvcResult.getResponse().getContentAsString()))
.hasToString(String.valueOf(one.getId()));
assertThat((Object)JsonPath.compile("[1]_links.self.href").read(mvcResult.getResponse().getContentAsString()))
assertThat((Object) JsonPath.compile("[1]_links.self.href").read(mvcResult.getResponse().getContentAsString()))
.hasToString("http://localhost/rest/v1/distributionsets/" + two.getId());
assertThat((Object)JsonPath.compile("[1]id").read(mvcResult.getResponse().getContentAsString()))
assertThat((Object) JsonPath.compile("[1]id").read(mvcResult.getResponse().getContentAsString()))
.hasToString(String.valueOf(two.getId()));
assertThat((Object)JsonPath.compile("[2]_links.self.href").read(mvcResult.getResponse().getContentAsString()))
assertThat((Object) JsonPath.compile("[2]_links.self.href").read(mvcResult.getResponse().getContentAsString()))
.hasToString("http://localhost/rest/v1/distributionsets/" + three.getId());
assertThat((Object)JsonPath.compile("[2]id").read(mvcResult.getResponse().getContentAsString()))
assertThat((Object) JsonPath.compile("[2]id").read(mvcResult.getResponse().getContentAsString()))
.hasToString(String.valueOf(three.getId()));
// check in database
@@ -1105,23 +1105,14 @@ class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegrationTe
metaData1.put(new JSONObject().put("key", knownKey1).put("value", knownValue1));
metaData1.put(new JSONObject().put("key", knownKey2).put("value", knownValue2));
mvc.perform(post("/rest/v1/distributionsets/{dsId}/metadata", testDS.getId()).accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON).content(metaData1.toString()))
mvc.perform(post("/rest/v1/distributionsets/{dsId}/metadata", testDS.getId())
.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("[1]key", equalTo(knownKey2)))
.andExpect(jsonPath("[1]value", equalTo(knownValue2)));
.andExpect(status().isCreated());
final DistributionSetMetadata metaKey1 = distributionSetManagement
.findMetaDataByDistributionSetId(testDS.getId(), knownKey1).get();
final DistributionSetMetadata metaKey2 = distributionSetManagement
.findMetaDataByDistributionSetId(testDS.getId(), knownKey2).get();
assertThat(metaKey1.getValue()).isEqualTo(knownValue1);
assertThat(metaKey2.getValue()).isEqualTo(knownValue2);
assertThat(distributionSetManagement.getMetadata(testDS.getId()).get(knownKey1)).isEqualTo(knownValue1);
assertThat(distributionSetManagement.getMetadata(testDS.getId()).get(knownKey2)).isEqualTo(knownValue2);
// verify quota enforcement
final int maxMetaData = quotaManagement.getMaxMetaDataEntriesPerDistributionSet();
@@ -1131,17 +1122,14 @@ class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegrationTe
metaData2.put(new JSONObject().put("key", knownKey1 + i).put("value", knownValue1 + i));
}
mvc.perform(post("/rest/v1/distributionsets/{dsId}/metadata", testDS.getId()).accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON).content(metaData2.toString()))
mvc.perform(post("/rest/v1/distributionsets/{dsId}/metadata", testDS.getId())
.contentType(MediaType.APPLICATION_JSON)
.content(metaData2.toString()))
.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(distributionSetManagement
.findMetaDataByDistributionSetId(testDS.getId(), PageRequest.of(0, Integer.MAX_VALUE))
.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(distributionSetManagement.getMetadata(testDS.getId())).hasSize(metaData1.length());
}
@Test
@@ -1151,25 +1139,18 @@ class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegrationTe
final String knownKey = "knownKey";
final String knownValue = "knownValue";
final String updateValue = "valueForUpdate";
final DistributionSet testDS = testdataFactory.createDistributionSet("one");
createDistributionSetMetadata(testDS.getId(), entityFactory.generateDsMetadata(knownKey, knownValue));
distributionSetManagement.createMetadata(testDS.getId(), Map.of(knownKey, knownValue));
final JSONObject jsonObject = new JSONObject().put("key", knownKey).put("value", updateValue);
mvc.perform(put("/rest/v1/distributionsets/{dsId}/metadata/{key}", testDS.getId(), knownKey)
.accept(MediaType.APPLICATION_JSON).contentType(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)));
final DistributionSetMetadata assertDS = distributionSetManagement
.findMetaDataByDistributionSetId(testDS.getId(), knownKey).get();
assertThat(assertDS.getValue()).isEqualTo(updateValue);
.andExpect(status().isOk());
assertThat(distributionSetManagement.getMetadata(testDS.getId()).get(knownKey)).isEqualTo(updateValue);
}
@Test
@@ -1180,13 +1161,18 @@ class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegrationTe
final String knownValue = "knownValue";
final DistributionSet testDS = testdataFactory.createDistributionSet("one");
createDistributionSetMetadata(testDS.getId(), entityFactory.generateDsMetadata(knownKey, knownValue));
distributionSetManagement.createMetadata(testDS.getId(), Map.of(knownKey, knownValue));
mvc.perform(delete("/rest/v1/distributionsets/{dsId}/metadata/{key}", testDS.getId(), knownKey))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk());
assertThat(distributionSetManagement.findMetaDataByDistributionSetId(testDS.getId(), knownKey)).isNotPresent();
// already deleted
mvc.perform(delete("/rest/v1/distributionsets/{dsId}/metadata/{key}", testDS.getId(), knownKey))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isNotFound());
assertThat(distributionSetManagement.getMetadata(testDS.getId()).get(knownKey)).isNull();
}
@Test
@@ -1195,9 +1181,8 @@ class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegrationTe
// prepare and create metadata for deletion
final String knownKey = "knownKey";
final String knownValue = "knownValue";
final DistributionSet testDS = testdataFactory.createDistributionSet("one");
createDistributionSetMetadata(testDS.getId(), entityFactory.generateDsMetadata(knownKey, knownValue));
distributionSetManagement.createMetadata(testDS.getId(), Map.of(knownKey, knownValue));
mvc.perform(delete("/rest/v1/distributionsets/{dsId}/metadata/XXX", testDS.getId(), knownKey))
.andDo(MockMvcResultPrinter.print())
@@ -1207,17 +1192,17 @@ class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegrationTe
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isNotFound());
assertThat(distributionSetManagement.findMetaDataByDistributionSetId(testDS.getId(), knownKey)).isPresent();
assertThat(distributionSetManagement.getMetadata(testDS.getId()).get(knownKey)).isNotNull();
}
@Test
@Description("Ensures that a metadata entry selection through API reflects the repository content.")
void geteMetadataKey() throws Exception {
void getMetadataKey() throws Exception {
// prepare and create metadata
final String knownKey = "knownKey";
final String knownValue = "knownValue";
final DistributionSet testDS = testdataFactory.createDistributionSet("one");
createDistributionSetMetadata(testDS.getId(), entityFactory.generateDsMetadata(knownKey, knownValue));
distributionSetManagement.createMetadata(testDS.getId(), Map.of(knownKey, knownValue));
mvc.perform(get("/rest/v1/distributionsets/{dsId}/metadata/{key}", testDS.getId(), knownKey))
.andDo(MockMvcResultPrinter.print())
@@ -1234,43 +1219,15 @@ class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegrationTe
final String knownValuePrefix = "knownValue";
final DistributionSet testDS = testdataFactory.createDistributionSet("one");
for (int index = 0; index < totalMetadata; index++) {
distributionSetManagement.putMetaData(testDS.getId(),
List.of(entityFactory.generateDsMetadata(knownKeyPrefix + index, knownValuePrefix + index)));
distributionSetManagement.createMetadata(testDS.getId(), Map.of(knownKeyPrefix + index, knownValuePrefix + index));
}
mvc.perform(get(MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING + "/{distributionSetId}/metadata",
testDS.getId()))
mvc.perform(get(MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING + "/{distributionSetId}/metadata", testDS.getId()))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(content().contentType(MediaTypes.HAL_JSON));
}
@Test
@Description("Ensures that a metadata entry paged list selection through API reflectes the repository content.")
void getPagedListOfMetadata() throws Exception {
final int totalMetadata = 10;
final int limitParam = 5;
final String offsetParam = "0";
final String knownKeyPrefix = "knownKey";
final String knownValuePrefix = "knownValue";
final DistributionSet testDS = testdataFactory.createDistributionSet("one");
for (int index = 0; index < totalMetadata; index++) {
createDistributionSetMetadata(testDS.getId(),
entityFactory.generateDsMetadata(knownKeyPrefix + index, knownValuePrefix + index));
}
mvc.perform(get("/rest/v1/distributionsets/{dsId}/metadata?offset=" + offsetParam + "&limit=" + limitParam,
testDS.getId()))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(jsonPath("size", equalTo(limitParam)))
.andExpect(jsonPath("total", equalTo(totalMetadata)))
.andExpect(jsonPath("content[0].key", equalTo("knownKey0")))
.andExpect(jsonPath("content[0].value", equalTo("knownValue0")));
}
@Test
@Description("Ensures that a DS search with query parameters returns the expected result.")
void searchDistributionSetRsql() throws Exception {
@@ -1334,29 +1291,6 @@ class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegrationTe
.andExpect(jsonPath("content[0].controllerId", equalTo("1")));
}
@Test
@Description("Ensures that a DS metadata filtered query with value==knownValue1 parameter returns only the metadata entries with that value.")
void searchDistributionSetMetadataRsql() throws Exception {
final int totalMetadata = 10;
final String knownKeyPrefix = "knownKey";
final String knownValuePrefix = "knownValue";
final DistributionSet testDS = testdataFactory.createDistributionSet("one");
for (int index = 0; index < totalMetadata; index++) {
createDistributionSetMetadata(testDS.getId(),
entityFactory.generateDsMetadata(knownKeyPrefix + index, knownValuePrefix + index));
}
final String rsqlSearchValue1 = "value==knownValue1";
mvc.perform(get("/rest/v1/distributionsets/{dsId}/metadata?q=" + rsqlSearchValue1, testDS.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")));
}
@Test
@Description("Ensures that multi target assignment through API is reflected by the repository in the case of "
+ "DOWNLOAD_ONLY.")

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import jakarta.validation.constraints.NotEmpty;
@@ -30,7 +31,6 @@ import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldExc
import org.eclipse.hawkbit.repository.exception.UnsupportedSoftwareModuleForThisDistributionSetException;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetFilter;
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.MetaData;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
@@ -124,29 +124,38 @@ public interface DistributionSetManagement extends RepositoryManagement<Distribu
List<DistributionSet> unassignTag(@NotEmpty Collection<Long> ids, long tagId);
/**
* Creates a list of distribution set meta-data entries.
* Creates a map of distribution set meta-data entries.
*
* @param id if the {@link DistributionSet} the metadata has to be created for
* @param metadata the meta-data entries to create or update
* @return the updated or created distribution set meta-data entries
* @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}
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
List<DistributionSetMetadata> putMetaData(long id, @NotEmpty Collection<MetaData> metadata);
void createMetadata(long id, @NotEmpty Map<String, String> metadata);
/**
* Updates a distribution set meta-data value if corresponding entry exists.
* Finds all meta-data by the given distribution set id.
*
* @param id the distribution set id to retrieve the meta-data from
* @return a paged result of all meta-data entries for a given distribution set id
* @throws EntityNotFoundException if distribution set with given ID does not exist
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
Map<String, String> getMetadata(long id);
/**
* Updates a distribution set meta-data values by adding them.
*
* @param id {@link DistributionSet} of the meta-data entry to be updated
* @param metadata meta-data entry to be updated
* @return the updated meta-data entry
* @param key meta data-entry key to be updated
* @param value meta data-entry to be new value
* @throws EntityNotFoundException in case the meta-data entry does not exist and cannot be updated
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
DistributionSetMetadata updateMetaData(long id, @NotNull MetaData metadata);
void updateMetadata(long id, @NotNull String key, @NotNull String value);
/**
* Deletes a distribution set meta-data entry.
@@ -156,7 +165,7 @@ public interface DistributionSetManagement extends RepositoryManagement<Distribu
* @throws EntityNotFoundException if given set does not exist
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
void deleteMetaData(long id, @NotEmpty String key);
void deleteMetadata(long id, @NotEmpty String key);
/**
* Locks a distribution set. From then on its functional properties could not be changed, and it could be assigned to targets
@@ -221,33 +230,6 @@ public interface DistributionSetManagement extends RepositoryManagement<Distribu
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
Optional<DistributionSet> findByNameAndVersion(@NotEmpty String distributionName, @NotEmpty String version);
/**
* Finds all meta-data by the given distribution set id.
*
* @param id the distribution set id to retrieve the meta-data from
* @param pageable the page request to page the result
* @return a paged result of all meta-data entries for a given distribution
* set id
* @throws EntityNotFoundException if distribution set with given ID does not exist
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
Page<DistributionSetMetadata> findMetaDataByDistributionSetId(long id, @NotNull Pageable pageable);
/**
* Finds all meta-data by the given distribution set id.
*
* @param id the distribution set id to retrieve the meta-data from
* @param rsqlParam rsql query string
* @param pageable the page request to page the result
* @return a paged result of all meta-data entries for a given distribution set 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 of distribution set with given ID does not exist
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
Page<DistributionSetMetadata> findMetaDataByDistributionSetIdAndRsql(long id, @NotNull String rsqlParam, @NotNull Pageable pageable);
/**
* Finds all {@link DistributionSet}s based on completeness.
*
@@ -295,25 +277,6 @@ public interface DistributionSetManagement extends RepositoryManagement<Distribu
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
Page<DistributionSet> findByRsqlAndTag(@NotNull String rsqlParam, long tagId, @NotNull Pageable pageable);
/**
* Finds a single distribution set meta-data by its id.
*
* @param id of the {@link DistributionSet}
* @param key of the meta-data element
* @return the found DistributionSetMetadata
* @throws EntityNotFoundException is set with given ID does not exist
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
Optional<DistributionSetMetadata> findMetaDataByDistributionSetId(long id, @NotEmpty String key);/**
* Counts all meta-data by the given distribution set id.
*
* @param id the distribution set id to retrieve the meta-data count from
* @return count of ds metadata
* @throws EntityNotFoundException if distribution set with given ID does not exist
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
long countMetaDataByDistributionSetId(long id);
/**
* Counts all {@link DistributionSet}s based on completeness.
*

View File

@@ -9,9 +9,6 @@
*/
package org.eclipse.hawkbit.repository;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import org.eclipse.hawkbit.repository.builder.ActionStatusBuilder;
import org.eclipse.hawkbit.repository.builder.DistributionSetBuilder;
import org.eclipse.hawkbit.repository.builder.DistributionSetTypeBuilder;
@@ -43,17 +40,6 @@ public interface EntityFactory {
*/
DistributionSetBuilder distributionSet();
/**
* Generates an {@link MetaData} element for distribution set without
* persisting it.
*
* @param key {@link MetaData#getKey()}
* @param value {@link MetaData#getValue()}
* @return {@link MetaData} object
*/
MetaData generateDsMetadata(@Size(min = 1, max = MetaData.KEY_MAX_SIZE) @NotNull String key,
@Size(max = MetaData.VALUE_MAX_SIZE) String value);
/**
* @return {@link SoftwareModuleMetadataBuilder} object
*/

View File

@@ -23,6 +23,16 @@ import java.util.Set;
*/
public interface DistributionSet extends NamedVersionedEntity {
/**
* Maximum length of metadata key.
*/
int METADATA_KEY_SIZE = 128;
/**
* Maximum length of metadata value.
*/
int METADATA_VALUE_SIZE = 4000;
/**
* @return type of the {@link DistributionSet}.
*/

View File

@@ -1,26 +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;
/**
* {@link MetaData} of a {@link DistributionSet}.
*/
public interface DistributionSetMetadata extends MetaData {
/**
* @return {@link DistributionSet} of this {@link MetaData} entry.
*/
DistributionSet getDistributionSet();
@Override
default Long getEntityId() {
return getDistributionSet().getId();
}
}

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.jpa.model.JpaDistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.MetaData;
import org.springframework.validation.annotation.Validated;
@@ -71,11 +70,6 @@ public class JpaEntityFactory implements EntityFactory {
return distributionSetBuilder;
}
@Override
public MetaData generateDsMetadata(final String key, final String value) {
return new JpaDistributionSetMetadata(key, value == null ? null : value.strip());
}
@Override
public SoftwareModuleMetadataBuilder softwareModuleMetadata() {
return softwareModuleMetadataBuilder;

View File

@@ -119,7 +119,6 @@ import org.eclipse.hawkbit.repository.jpa.model.helper.SecurityTokenGeneratorHol
import org.eclipse.hawkbit.repository.jpa.model.helper.TenantAwareHolder;
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
import org.eclipse.hawkbit.repository.jpa.repository.ActionStatusRepository;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetMetadataRepository;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetRepository;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetTagRepository;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetTypeRepository;
@@ -536,7 +535,6 @@ public class RepositoryApplicationConfiguration {
final DistributionSetRepository distributionSetRepository,
final DistributionSetTagManagement distributionSetTagManagement, final SystemManagement systemManagement,
final DistributionSetTypeManagement distributionSetTypeManagement, final QuotaManagement quotaManagement,
final DistributionSetMetadataRepository distributionSetMetadataRepository,
final TargetRepository targetRepository,
final TargetFilterQueryRepository targetFilterQueryRepository, final ActionRepository actionRepository,
final SystemSecurityContext systemSecurityContext, final TenantConfigurationManagement tenantConfigurationManagement,
@@ -544,7 +542,7 @@ public class RepositoryApplicationConfiguration {
final DistributionSetTagRepository distributionSetTagRepository,
final JpaProperties properties, final RepositoryProperties repositoryProperties) {
return new JpaDistributionSetManagement(entityManager, distributionSetRepository, distributionSetTagManagement,
systemManagement, distributionSetTypeManagement, quotaManagement, distributionSetMetadataRepository,
systemManagement, distributionSetTypeManagement, quotaManagement,
targetRepository, targetFilterQueryRepository, actionRepository,
TenantConfigHelper.usingContext(systemSecurityContext, tenantConfigurationManagement),
virtualPropertyReplacer, softwareModuleRepository, distributionSetTagRepository,

View File

@@ -12,9 +12,9 @@ package org.eclipse.hawkbit.repository.jpa.management;
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.IMPLICIT_LOCK_ENABLED;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -24,11 +24,15 @@ import java.util.function.Function;
import java.util.stream.Collectors;
import jakarta.persistence.EntityManager;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.MapJoin;
import jakarta.persistence.criteria.Root;
import jakarta.persistence.metamodel.MapAttribute;
import jakarta.validation.constraints.NotNull;
import org.eclipse.hawkbit.repository.DistributionSetFields;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.DistributionSetMetadataFields;
import org.eclipse.hawkbit.repository.DistributionSetTagManagement;
import org.eclipse.hawkbit.repository.DistributionSetTypeManagement;
import org.eclipse.hawkbit.repository.QuotaManagement;
@@ -49,14 +53,10 @@ import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.eclipse.hawkbit.repository.jpa.builder.JpaDistributionSetCreate;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity_;
import org.eclipse.hawkbit.repository.jpa.model.DsMetadataCompositeKey;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetMetadata;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetMetadata_;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetMetadataRepository;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetRepository;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetTagRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleRepository;
@@ -69,10 +69,8 @@ import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetFilter;
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.MetaData;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Statistic;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
@@ -103,7 +101,6 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
private final SystemManagement systemManagement;
private final DistributionSetTypeManagement distributionSetTypeManagement;
private final QuotaManagement quotaManagement;
private final DistributionSetMetadataRepository distributionSetMetadataRepository;
private final TargetRepository targetRepository;
private final TargetFilterQueryRepository targetFilterQueryRepository;
private final ActionRepository actionRepository;
@@ -120,7 +117,6 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
final DistributionSetRepository distributionSetRepository,
final DistributionSetTagManagement distributionSetTagManagement, final SystemManagement systemManagement,
final DistributionSetTypeManagement distributionSetTypeManagement, final QuotaManagement quotaManagement,
final DistributionSetMetadataRepository distributionSetMetadataRepository,
final TargetRepository targetRepository,
final TargetFilterQueryRepository targetFilterQueryRepository, final ActionRepository actionRepository,
final TenantConfigHelper tenantConfigHelper,
@@ -135,7 +131,6 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
this.systemManagement = systemManagement;
this.distributionSetTypeManagement = distributionSetTypeManagement;
this.quotaManagement = quotaManagement;
this.distributionSetMetadataRepository = distributionSetMetadataRepository;
this.targetRepository = targetRepository;
this.targetFilterQueryRepository = targetFilterQueryRepository;
this.actionRepository = actionRepository;
@@ -360,21 +355,6 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
});
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSetMetadata updateMetaData(final long id, final MetaData md) {
// check if exists otherwise throw entity not found exception
final JpaDistributionSetMetadata toUpdate = (JpaDistributionSetMetadata) findMetaDataByDistributionSetId(id, md.getKey())
.orElseThrow(() -> new EntityNotFoundException(DistributionSetMetadata.class, id, md.getKey()));
toUpdate.setValue(md.getValue());
// touch it to update the lock revision because we are modifying the DS indirectly, it will, also check UPDATE access
JpaManagementHelper.touch(entityManager, distributionSetRepository, (JpaDistributionSet) getValid(id));
return distributionSetMetadataRepository.save(toUpdate);
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
@@ -394,34 +374,60 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<DistributionSetMetadata> putMetaData(final long id, final Collection<MetaData> md) {
public void createMetadata(final long id, final Map<String, String> md) {
final JpaDistributionSet distributionSet = (JpaDistributionSet) getValid(id);
assertMetaDataQuota(id, md.size());
md.forEach(meta -> checkAndThrowIfDistributionSetMetadataAlreadyExists(
new DsMetadataCompositeKey(id, meta.getKey())));
// get the modifiable metadata map
final Map<String, String> metadata = distributionSet.getMetadata();
md.keySet().forEach(key -> {
if (metadata.containsKey(key)) {
throw new EntityAlreadyExistsException("Metadata entry with key '" + key + "' already exists");
}
});
metadata.putAll(md);
JpaManagementHelper.touch(entityManager, distributionSetRepository, distributionSet);
assertMetaDataQuota(id, metadata.size());
return md.stream()
.map(meta -> distributionSetMetadataRepository
.save(new JpaDistributionSetMetadata(meta.getKey(), distributionSet, meta.getValue())))
.collect(Collectors.toUnmodifiableList());
distributionSetRepository.save(distributionSet);
}
@Override
public Map<String, String> getMetadata(final long id) {
assertDistributionSetExists(id);
return getMap(id, JpaDistributionSet_.metadata);
}
@Override
@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 JpaDistributionSetMetadata metadata = (JpaDistributionSetMetadata) findMetaDataByDistributionSetId(
id, key)
.orElseThrow(() -> new EntityNotFoundException(DistributionSetMetadata.class, id, key));
public void updateMetadata(final long id, final String key, final String value) {
final JpaDistributionSet distributionSet = (JpaDistributionSet) getValid(id);
// touch it to update the lock revision because we are modifying the
// DS indirectly, it will, also check UPDATE access
JpaManagementHelper.touch(entityManager, distributionSetRepository, metadata.getDistributionSet());
distributionSetMetadataRepository.deleteById(metadata.getId());
// get the modifiable metadata map
final Map<String, String> metadata = distributionSet.getMetadata();
if (!metadata.containsKey(key)) {
throw new EntityNotFoundException("DistributionSet metadata", id + ":" + key);
}
metadata.put(key, value);
distributionSetRepository.save(distributionSet);
}
@Override
@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 JpaDistributionSet distributionSet = (JpaDistributionSet) getValid(id);
// get the modifiable metadata map
final Map<String, String> metadata = distributionSet.getMetadata();
if (metadata.remove(key) == null) {
throw new EntityNotFoundException("DistributionSet metadata", id + ":" + key);
}
distributionSetRepository.save(distributionSet);
}
@Override
@@ -504,33 +510,6 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
return distributionSet;
}
@Override
public Page<DistributionSetMetadata> findMetaDataByDistributionSetId(final long id, final Pageable pageable) {
assertDistributionSetExists(id);
return JpaManagementHelper.findAllWithCountBySpec(distributionSetMetadataRepository, Collections.singletonList(byDsIdSpec(id)), pageable
);
}
@Override
public long countMetaDataByDistributionSetId(final long id) {
assertDistributionSetExists(id);
return distributionSetMetadataRepository.countByDistributionSetId(id);
}
@Override
public Page<DistributionSetMetadata> findMetaDataByDistributionSetIdAndRsql(final long id, final String rsqlParam,
final Pageable pageable) {
assertDistributionSetExists(id);
final List<Specification<JpaDistributionSetMetadata>> specList = Arrays
.asList(RSQLUtility.buildRsqlSpecification(rsqlParam, DistributionSetMetadataFields.class,
virtualPropertyReplacer, database), byDsIdSpec(id));
return JpaManagementHelper.findAllWithCountBySpec(distributionSetMetadataRepository, specList, pageable);
}
@Override
public Slice<DistributionSet> findByCompleted(final Pageable pageReq, final Boolean complete) {
final List<Specification<JpaDistributionSet>> specifications = buildSpecsByComplete(complete);
@@ -577,15 +556,6 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
DistributionSetSpecification.hasTag(tagId), DistributionSetSpecification.isNotDeleted()), pageable);
}
@Override
public Optional<DistributionSetMetadata> findMetaDataByDistributionSetId(final long id, final String key) {
assertDistributionSetExists(id);
return distributionSetMetadataRepository
.findById(new DsMetadataCompositeKey(id, key))
.map(DistributionSetMetadata.class::cast);
}
@Override
public long countByTypeId(final long typeId) {
if (!distributionSetTypeManagement.exists(typeId)) {
@@ -732,6 +702,24 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
}
}
private Map<String, String> getMap(final long id, final MapAttribute<JpaDistributionSet, String, String> mapAttribute) {
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaQuery<Object[]> query = cb.createQuery(Object[].class);
final Root<JpaDistributionSet> targetRoot = query.from(JpaDistributionSet.class);
query.where(cb.equal(targetRoot.get(AbstractJpaBaseEntity_.ID), id));
final MapJoin<JpaDistributionSet, String, String> mapJoin = targetRoot.join(mapAttribute);
query.multiselect(mapJoin.key(), mapJoin.value());
query.orderBy(cb.asc(mapJoin.key()));
return entityManager
.createQuery(query)
.getResultList()
.stream()
.collect(Collectors.toMap(entry -> (String) entry[0], entry -> (String) entry[1], (v1, v2) -> v1, LinkedHashMap::new));
}
private JpaSoftwareModule findSoftwareModuleAndThrowExceptionIfNotFound(final Long softwareModuleId) {
return softwareModuleRepository.findById(softwareModuleId)
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, softwareModuleId));
@@ -754,9 +742,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
}
private void assertMetaDataQuota(final Long dsId, final int requested) {
QuotaHelper.assertAssignmentQuota(dsId, requested, quotaManagement.getMaxMetaDataEntriesPerDistributionSet(),
DistributionSetMetadata.class, DistributionSet.class,
distributionSetMetadataRepository::countByDistributionSetId);
final int limit = quotaManagement.getMaxMetaDataEntriesPerDistributionSet();
QuotaHelper.assertAssignmentQuota(dsId, requested, limit, "Metadata", DistributionSet.class.getSimpleName(), null);
}
private void assertSoftwareModuleQuota(final Long id, final int requested) {
@@ -764,11 +751,6 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
SoftwareModule.class, DistributionSet.class, softwareModuleRepository::countByAssignedToId);
}
private Specification<JpaDistributionSetMetadata> byDsIdSpec(final long dsId) {
return (root, query, cb) -> cb
.equal(root.get(JpaDistributionSetMetadata_.distributionSet).get(AbstractJpaBaseEntity_.id), dsId);
}
private void assertDistributionSetIsNotAssignedToTargets(final Long distributionSet) {
if (actionRepository.countByDistributionSetId(distributionSet) > 0) {
throw new EntityReadOnlyException(String.format(
@@ -776,13 +758,6 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
}
}
private void checkAndThrowIfDistributionSetMetadataAlreadyExists(final DsMetadataCompositeKey metadataId) {
if (distributionSetMetadataRepository.existsById(metadataId)) {
throw new EntityAlreadyExistsException(
"Metadata entry with key '" + metadataId.getKey() + "' already exists");
}
}
private List<JpaDistributionSet> getDistributionSets(final Collection<Long> ids) {
final List<JpaDistributionSet> foundDs = distributionSetRepository.findAllById(ids);
if (foundDs.size() != ids.size()) {

View File

@@ -1,76 +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.io.Serializable;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* The DistributionSet Metadata composite key which contains the meta-data key and the ID of the DistributionSet itself.
*/
@NoArgsConstructor // Default constructor for JPA
@Setter
@Getter
public final class DsMetadataCompositeKey implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private String key;
private Long distributionSet;
/**
* @param distributionSet the distribution set for this meta data
* @param key the key of the meta data
*/
public DsMetadataCompositeKey(final Long distributionSet, final String key) {
this.distributionSet = distributionSet;
this.key = key;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (distributionSet == null ? 0 : distributionSet.hashCode());
result = prime * result + (key == null ? 0 : key.hashCode());
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final DsMetadataCompositeKey other = (DsMetadataCompositeKey) obj;
if (distributionSet == null) {
if (other.distributionSet != null) {
return false;
}
} else if (!distributionSet.equals(other.distributionSet)) {
return false;
}
if (key == null) {
return other.key == null;
} else {
return key.equals(other.key);
}
}
}

View File

@@ -13,13 +13,14 @@ import java.io.Serial;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
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;
@@ -28,9 +29,9 @@ import jakarta.persistence.JoinColumn;
import jakarta.persistence.JoinTable;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.MapKeyColumn;
import jakarta.persistence.NamedAttributeNode;
import jakarta.persistence.NamedEntityGraph;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import jakarta.persistence.UniqueConstraint;
import jakarta.validation.constraints.NotNull;
@@ -47,7 +48,6 @@ import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetExcepti
import org.eclipse.hawkbit.repository.exception.LockedException;
import org.eclipse.hawkbit.repository.exception.UnsupportedSoftwareModuleForThisDistributionSetException;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
@@ -109,11 +109,16 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen
foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_tag_tag")) })
private Set<DistributionSetTag> tags = new HashSet<>();
@ToString.Exclude
@OneToMany(mappedBy = "distributionSet", fetch = FetchType.LAZY,
cascade = { CascadeType.REMOVE },
targetEntity = JpaDistributionSetMetadata.class)
private List<DistributionSetMetadata> metadata;
// no cascade option on an ElementCollection, the target objects are always persisted, merged, removed with their parent
@Getter
@ElementCollection
@CollectionTable(
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)
private Map<String, String> metadata;
@Column(name = "complete")
private boolean complete;
@@ -217,14 +222,6 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen
return tags.remove(tag);
}
public List<DistributionSetMetadata> getMetadata() {
if (metadata == null) {
return Collections.emptyList();
}
return Collections.unmodifiableList(metadata);
}
public void lock() {
if (!isComplete()) {
throw new IncompleteDistributionSetException("Could not be locked while incomplete!");

View File

@@ -1,86 +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 jakarta.persistence.ConstraintMode;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.ForeignKey;
import jakarta.persistence.Id;
import jakarta.persistence.IdClass;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
/**
* Meta data for {@link DistributionSet}.
*/
@NoArgsConstructor // Default constructor needed for JPA entities.
@Getter
@IdClass(DsMetadataCompositeKey.class)
@Entity
@Table(name = "sp_ds_metadata")
public class JpaDistributionSetMetadata extends AbstractJpaMetaData implements DistributionSetMetadata {
@Serial
private static final long serialVersionUID = 1L;
@Id
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "ds", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_metadata_ds"))
private JpaDistributionSet distributionSet;
public JpaDistributionSetMetadata(final String key, final String value) {
super(key, value);
}
public JpaDistributionSetMetadata(final String key, final DistributionSet distributionSet, final String value) {
super(key, value);
this.distributionSet = (JpaDistributionSet) distributionSet;
}
public DsMetadataCompositeKey getId() {
return new DsMetadataCompositeKey(distributionSet.getId(), getKey());
}
public void setDistributionSet(final DistributionSet distributionSet) {
this.distributionSet = (JpaDistributionSet) distributionSet;
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((distributionSet == null) ? 0 : distributionSet.hashCode());
return result;
}
@Override
// exception squid:S2259 - obj is checked for null in super
@SuppressWarnings("squid:S2259")
public boolean equals(final Object obj) {
if (!super.equals(obj)) {
return false;
}
final JpaDistributionSetMetadata other = (JpaDistributionSetMetadata) obj;
if (distributionSet == null) {
return other.distributionSet == null;
} else {
return distributionSet.equals(other.distributionSet);
}
}
}

View File

@@ -1,39 +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.repository;
import org.eclipse.hawkbit.repository.jpa.model.DsMetadataCompositeKey;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;
/**
* {@link DistributionSetMetadata} repository.
*/
@Transactional(readOnly = true)
public interface DistributionSetMetadataRepository
extends PagingAndSortingRepository<JpaDistributionSetMetadata, DsMetadataCompositeKey>,
CrudRepository<JpaDistributionSetMetadata, DsMetadataCompositeKey>,
JpaSpecificationExecutor<JpaDistributionSetMetadata> {
/**
* Counts the meta data entries that match the given distribution set ID.
* <p/>
* No access control applied
*
* @param id of the distribution set.
* @return The number of matching meta data entries.
*/
long countByDistributionSetId(@Param("id") Long id);
}

View File

@@ -16,6 +16,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import jakarta.persistence.criteria.Predicate;
@@ -28,14 +29,12 @@ import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetMetadata;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetFilter;
import org.eclipse.hawkbit.repository.model.MetaData;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.junit.jupiter.api.Test;
@@ -121,12 +120,18 @@ class DistributionSetAccessControllerTest extends AbstractAccessControllerTest {
@Description("Verifies read access rules for distribution sets")
void verifyDistributionSetUpdates() {
// permit all operations first to prepare test setup
permitAllOperations(AccessController.Operation.READ);
permitAllOperations(AccessController.Operation.CREATE);
permitAllOperations(AccessController.Operation.UPDATE);
final DistributionSet permitted = testdataFactory.createDistributionSet();
final String mdPresetKey = "metadata.preset";
final String mdPresetValue = "presetValue";
distributionSetManagement.createMetadata(permitted.getId(), Map.of(mdPresetKey, mdPresetValue));
final DistributionSet readOnly = testdataFactory.createDistributionSet();
distributionSetManagement.createMetadata(readOnly.getId(), Map.of(mdPresetKey, mdPresetValue));
final DistributionSet hidden = testdataFactory.createDistributionSet();
distributionSetManagement.createMetadata(hidden.getId(), Map.of(mdPresetKey, mdPresetValue));
final SoftwareModule swModule = testdataFactory.createSoftwareModuleOs();
@@ -134,52 +139,49 @@ class DistributionSetAccessControllerTest extends AbstractAccessControllerTest {
testAccessControlManger.deleteAllRules();
// define access controlling rule
defineAccess(AccessController.Operation.READ, permitted, readOnly);
// allow updating the permitted distributionSet
defineAccess(AccessController.Operation.READ, permitted);
defineAccess(AccessController.Operation.UPDATE, permitted);
// verify distributionSetManagement#assignSoftwareModules
final var singleModuleIdList = Collections.singletonList(swModule.getId());
final List<Long> singleModuleIdList = Collections.singletonList(swModule.getId());
assertThat(distributionSetManagement.assignSoftwareModules(permitted.getId(), singleModuleIdList))
.satisfies(ds -> assertThat(ds.getModules().stream().map(Identifiable::getId).toList()).contains(swModule.getId()));
final Long readOnlyId = readOnly.getId();
assertThatThrownBy(() -> distributionSetManagement.assignSoftwareModules(readOnlyId, singleModuleIdList))
.as("Distribution set not allowed to me modified.")
.isInstanceOf(EntityNotFoundException.class);
.isInstanceOf(InsufficientPermissionException.class);
final Long hiddenId = hidden.getId();
assertThatThrownBy(() -> distributionSetManagement.assignSoftwareModules(hiddenId, singleModuleIdList))
.as("Distribution set should not be visible.")
.isInstanceOf(EntityNotFoundException.class);
final JpaDistributionSetMetadata metadata = new JpaDistributionSetMetadata("test", "test");
final Map<String, String> metadata = Map.of("test.create", mdPresetValue);
// verify distributionSetManagement#createMetaData
final List<MetaData> metadataList = Collections.singletonList(metadata);
distributionSetManagement.putMetaData(permitted.getId(), metadataList);
assertThatThrownBy(() -> distributionSetManagement.putMetaData(readOnlyId, metadataList))
.as("Distribution set not allowed to me modified.")
.isInstanceOf(EntityNotFoundException.class);
assertThatThrownBy(() -> distributionSetManagement.putMetaData(hiddenId, metadataList))
distributionSetManagement.createMetadata(permitted.getId(), metadata);
assertThatThrownBy(() -> distributionSetManagement.createMetadata(readOnlyId, metadata))
.as("Distribution set not allowed to be modified.")
.isInstanceOf(InsufficientPermissionException.class);
assertThatThrownBy(() -> distributionSetManagement.createMetadata(hiddenId, metadata))
.as("Distribution set should not be visible.")
.isInstanceOf(EntityNotFoundException.class);
// verify distributionSetManagement#updateMetaData
distributionSetManagement.updateMetaData(permitted.getId(), metadata);
assertThatThrownBy(() -> distributionSetManagement.updateMetaData(readOnlyId, metadata))
final String newValue = "newValue";
distributionSetManagement.updateMetadata(permitted.getId(), mdPresetKey, newValue);
assertThatThrownBy(() -> distributionSetManagement.updateMetadata(readOnlyId, mdPresetKey, newValue))
.as("Distribution set not allowed to me modified.")
.isInstanceOf(EntityNotFoundException.class);
assertThatThrownBy(() -> distributionSetManagement.updateMetaData(hiddenId, metadata))
.isInstanceOf(InsufficientPermissionException.class);
assertThatThrownBy(() -> distributionSetManagement.updateMetadata(hiddenId, mdPresetKey, newValue))
.as("Distribution set should not be visible.")
.isInstanceOf(EntityNotFoundException.class);
// verify distributionSetManagement#deleteMetaData
final String metadataKey = metadata.getKey();
distributionSetManagement.deleteMetaData(permitted.getId(), metadataKey);
assertThatThrownBy(() -> distributionSetManagement.deleteMetaData(readOnlyId, metadataKey))
final String metadataKey = metadata.entrySet().stream().findAny().get().getKey();
distributionSetManagement.deleteMetadata(permitted.getId(), metadataKey);
assertThatThrownBy(() -> distributionSetManagement.deleteMetadata(readOnlyId, mdPresetKey))
.as("Distribution set not allowed to me modified.")
.isInstanceOf(EntityNotFoundException.class);
assertThatThrownBy(() -> distributionSetManagement.deleteMetaData(hiddenId, metadataKey))
.isInstanceOf(InsufficientPermissionException.class);
assertThatThrownBy(() -> distributionSetManagement.deleteMetadata(hiddenId, mdPresetKey))
.as("Distribution set should not be visible.")
.isInstanceOf(EntityNotFoundException.class);
}

View File

@@ -220,13 +220,12 @@ class TargetAccessControllerTest extends AbstractAccessControllerTest {
final Target permittedTarget = targetManagement
.create(entityFactory.target().create().controllerId("device01").status(TargetUpdateStatus.REGISTERED));
final String hiddenTargetControllerId = targetManagement
.create(entityFactory.target().create().controllerId("device02").status(TargetUpdateStatus.REGISTERED))
.getControllerId();
// define access controlling rule
defineAccess(AccessController.Operation.READ, permittedTarget);
overwriteAccess(AccessController.Operation.READ, permittedTarget);
// verify targetManagement#findByUpdateStatus before assignment
assertThat(targetManagement.findByUpdateStatus(Pageable.unpaged(), TargetUpdateStatus.REGISTERED).get()
@@ -269,18 +268,15 @@ class TargetAccessControllerTest extends AbstractAccessControllerTest {
final Target manageableTarget = targetManagement
.create(entityFactory.target().create().controllerId("device01").status(TargetUpdateStatus.REGISTERED));
final Target readOnlyTarget = targetManagement
.create(entityFactory.target().create().controllerId("device02").status(TargetUpdateStatus.REGISTERED));
// define access controlling rule
defineAccess(AccessController.Operation.READ, manageableTarget, readOnlyTarget);
defineAccess(AccessController.Operation.UPDATE, manageableTarget);
// overwriting full access controlling rule
overwriteAccess(AccessController.Operation.READ, manageableTarget, readOnlyTarget);
overwriteAccess(AccessController.Operation.UPDATE, manageableTarget);
// assignment is permitted for manageableTarget
assertThat(assignDistributionSet(firstDsId, manageableTarget.getControllerId()).getAssigned())
.isEqualTo(1);
assertThat(assignDistributionSet(firstDsId, manageableTarget.getControllerId()).getAssigned()).isEqualTo(1);
// assignment is denied for readOnlyTarget (read, but no update permissions)
final var readOnlyTargetControllerId = readOnlyTarget.getControllerId();
@@ -309,19 +305,17 @@ class TargetAccessControllerTest extends AbstractAccessControllerTest {
final List<Target> updateTargets = testdataFactory.createTargets("update1", "update2", "update3");
final List<Target> readTargets = testdataFactory.createTargets("read1", "read2", "read3", "read4");
final List<Target> hiddenTargets = testdataFactory.createTargets(
"hidden1", "hidden2", "hidden3", "hidden4", "hidden5");
final List<Target> hiddenTargets = testdataFactory.createTargets("hidden1", "hidden2", "hidden3", "hidden4", "hidden5");
defineAccess(AccessController.Operation.UPDATE, updateTargets);
defineAccess(AccessController.Operation.READ, merge(readTargets, updateTargets));
overwriteAccess(AccessController.Operation.READ, merge(readTargets, updateTargets));
final Rollout rollout = testdataFactory.createRolloutByVariables("testRollout", "description",
updateTargets.size(), "id==*", ds, "50", "5");
final Rollout rollout = testdataFactory.createRolloutByVariables(
"testRollout", "description", updateTargets.size(), "id==*", ds, "50", "5");
assertThat(rollout.getTotalTargets()).isEqualTo(updateTargets.size());
final List<RolloutGroup> content = rolloutGroupManagement.findByRollout(rollout.getId(), Pageable.unpaged())
.getContent();
final List<RolloutGroup> content = rolloutGroupManagement.findByRollout(rollout.getId(), Pageable.unpaged()).getContent();
assertThat(content).hasSize(updateTargets.size());
final List<Target> rolloutTargets = content.stream().flatMap(
@@ -393,6 +387,18 @@ class TargetAccessControllerTest extends AbstractAccessControllerTest {
target -> ids.contains(target.getId()));
}
private void overwriteAccess(final AccessController.Operation operation, final Target... target) {
overwriteAccess(operation, List.of(target));
}
private void overwriteAccess(final AccessController.Operation operation, final List<Target> targets) {
final List<Long> ids = targets.stream().map(Target::getId).toList();
testAccessControlManger.overwriteAccessRule(
JpaTarget.class, operation,
TargetSpecifications.hasIdIn(ids),
target -> ids.contains(target.getId()));
}
private static Specification<JpaDistributionSet> dsById(final Long distid) {
return (dsRoot, query, cb) -> {
final Predicate predicate = cb.equal(dsRoot.get(JpaDistributionSet_.id), distid);

View File

@@ -14,6 +14,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
@@ -37,14 +38,9 @@ class TargetTypeAccessControllerTest extends AbstractAccessControllerTest {
@Test
@Description("Verifies read access rules for target types")
void verifyTargetTypeReadOperations() {
permitAllOperations(AccessController.Operation.READ);
permitAllOperations(AccessController.Operation.CREATE);
final TargetType permittedTargetType = targetTypeManagement
.create(entityFactory.targetType().create().name("type1"));
final TargetType hiddenTargetType = targetTypeManagement
.create(entityFactory.targetType().create().name("type2"));
final TargetType permittedTargetType = targetTypeManagement.create(entityFactory.targetType().create().name("type1"));
final TargetType hiddenTargetType = targetTypeManagement.create(entityFactory.targetType().create().name("type2"));
// define access controlling rule
defineAccess(AccessController.Operation.READ, permittedTargetType);
@@ -67,12 +63,12 @@ class TargetTypeAccessControllerTest extends AbstractAccessControllerTest {
assertThat(targetTypeManagement.count()).isEqualTo(1);
// verify targetTypeManagement#countByName
// assertThat(targetTypeManagement.countByName(permittedTargetType.getName())).isEqualTo(1);
// assertThat(targetTypeManagement.countByName(hiddenTargetType.getName())).isZero();
assertThat(targetTypeManagement.countByName(permittedTargetType.getName())).isEqualTo(1);
assertThat(targetTypeManagement.countByName(hiddenTargetType.getName())).isZero();
// verify targetTypeManagement#countByName
// assertThat(targetTypeManagement.countByName(permittedTargetType.getName())).isEqualTo(1);
// assertThat(targetTypeManagement.countByName(hiddenTargetType.getName())).isZero();
assertThat(targetTypeManagement.countByName(permittedTargetType.getName())).isEqualTo(1);
assertThat(targetTypeManagement.countByName(hiddenTargetType.getName())).isZero();
// verify targetTypeManagement#get by id
assertThat(targetTypeManagement.get(permittedTargetType.getId())).isPresent();
@@ -161,15 +157,11 @@ class TargetTypeAccessControllerTest extends AbstractAccessControllerTest {
.isInstanceOf(InsufficientPermissionException.class);
}
private void defineAccess(final AccessController.Operation operation, final TargetType... targetType) {
defineAccess(operation, List.of(targetType));
}
private void defineAccess(final AccessController.Operation operation, final List<TargetType> targetTypes) {
final List<Long> ids = targetTypes.stream().map(TargetType::getId).toList();
private void defineAccess(final AccessController.Operation operation, final TargetType... targetTypes) {
final List<Long> ids = Stream.of(targetTypes).map(TargetType::getId).toList();
testAccessControlManger.defineAccessRule(
JpaTargetType.class, operation,
TargetTypeSpecification.hasIdIn(ids),
targetType -> ids.contains(targetType.getId()));
}
}
}

View File

@@ -31,7 +31,23 @@ public class TestAccessControlManger {
public <T> void defineAccessRule(
final Class<T> ruleClass, final AccessController.Operation operation,
final Specification<T> specification, final Predicate<T> check) {
accessRules.put(new AccessRuleId<>(ruleClass, operation), new AccessRule<>(specification, check));
defineAccessRule(ruleClass, operation, specification, check, false);
}
public <T> void overwriteAccessRule(
final Class<T> ruleClass, final AccessController.Operation operation,
final Specification<T> specification, final Predicate<T> check) {
defineAccessRule(ruleClass, operation, specification, check, true);
}
private <T> void defineAccessRule(
final Class<T> ruleClass, final AccessController.Operation operation,
final Specification<T> specification, final Predicate<T> check, final boolean overwrite) {
final AccessRuleId<T> ruleId = new AccessRuleId<>(ruleClass, operation);
if (!overwrite && accessRules.containsKey(ruleId)) {
throw new IllegalStateException("Access rule already defined for " + ruleId + "! You should explicitly set overwrite to true.");
}
accessRules.put(ruleId, new AccessRule<>(specification, check));
}
public <T extends AbstractJpaBaseEntity> Specification<T> getAccessRule(final Class<T> ruleClass,
@@ -53,8 +69,7 @@ public class TestAccessControlManger {
} else {
for (final T entity : entities) {
if (!accessRule.checker.test(entity)) {
throw new InsufficientPermissionException(
"Access to " + ruleClass.getName() + "/" + entity + " not allowed by checker!");
throw new InsufficientPermissionException("Access to " + ruleClass.getName() + "/" + entity + " not allowed by checker!");
}
}
}
@@ -67,4 +82,4 @@ public class TestAccessControlManger {
private record AccessRuleId<T>(Class<T> ruleClass, AccessController.Operation operation) {}
private record AccessRule<T>(Specification<T> specification, Predicate<T> checker) {}
}
}

View File

@@ -10,6 +10,7 @@
package org.eclipse.hawkbit.repository.jpa.management;
import java.util.List;
import java.util.Map;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
@@ -66,17 +67,36 @@ class DistributionSetManagementSecurityTest
@Test
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
void createMetaDataPermissionsCheck() {
void createMetadataPermissionsCheck() {
assertPermissions(
() -> distributionSetManagement.putMetaData(1L, List.of(entityFactory.generateDsMetadata("key", "value"))),
() -> {
distributionSetManagement.createMetadata(1L, Map.of("key", "value"));
return null;
},
List.of(SpPermission.UPDATE_REPOSITORY));
}
@Test
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
void deleteMetaDataPermissionsCheck() {
void getMetadataPermissiosCheck() {
assertPermissions(() -> distributionSetManagement.getMetadata(1L), List.of(SpPermission.READ_REPOSITORY));
}
@Test
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
void updateMetadataPermissionsCheck() {
assertPermissions(() -> {
distributionSetManagement.deleteMetaData(1L, "key");
distributionSetManagement.updateMetadata(1L,"key", "value");
return null;
},
List.of(SpPermission.UPDATE_REPOSITORY));
}
@Test
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
void deleteMetadataPermissionsCheck() {
assertPermissions(() -> {
distributionSetManagement.deleteMetadata(1L, "key");
return null;
}, List.of(SpPermission.UPDATE_REPOSITORY));
}
@@ -135,25 +155,6 @@ class DistributionSetManagementSecurityTest
assertPermissions(() -> distributionSetManagement.getOrElseThrowException(1L), List.of(SpPermission.READ_REPOSITORY));
}
@Test
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
void findMetaDataByDistributionSetIdPermissionsCheck() {
assertPermissions(() -> distributionSetManagement.findMetaDataByDistributionSetId(1L, PAGE), List.of(SpPermission.READ_REPOSITORY));
}
@Test
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
void countMetaDataByDistributionSetIdPermissionsCheck() {
assertPermissions(() -> distributionSetManagement.countMetaDataByDistributionSetId(1L), List.of(SpPermission.READ_REPOSITORY));
}
@Test
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
void findMetaDataByDistributionSetIdAndRsqlPermissionsCheck() {
assertPermissions(() -> distributionSetManagement.findMetaDataByDistributionSetIdAndRsql(1L, "rsql", PAGE),
List.of(SpPermission.READ_REPOSITORY));
}
@Test
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
void findByCompletedPermissionsCheck() {
@@ -192,12 +193,6 @@ class DistributionSetManagementSecurityTest
assertPermissions(() -> distributionSetManagement.findByRsqlAndTag("rsql", 1L, PAGE), List.of(SpPermission.READ_REPOSITORY));
}
@Test
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
void getMetaDataByDistributionSetIdPermissionsCheck() {
assertPermissions(() -> distributionSetManagement.findMetaDataByDistributionSetId(1L, "key"), List.of(SpPermission.READ_REPOSITORY));
}
@Test
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
void isInUsePermissionsCheck() {
@@ -210,13 +205,6 @@ class DistributionSetManagementSecurityTest
assertPermissions(() -> distributionSetManagement.unassignSoftwareModule(1L, 1L), List.of(SpPermission.UPDATE_REPOSITORY));
}
@Test
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
void updateMetaDataPermissionsCheck() {
assertPermissions(() -> distributionSetManagement.updateMetaData(1L, entityFactory.generateDsMetadata("key", "value")),
List.of(SpPermission.UPDATE_REPOSITORY));
}
@Test
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
void countByTypeIdPermissionsCheck() {

View File

@@ -18,7 +18,9 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.Set;
@@ -49,17 +51,14 @@ import org.eclipse.hawkbit.repository.exception.LockedException;
import org.eclipse.hawkbit.repository.exception.UnsupportedSoftwareModuleForThisDistributionSetException;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetFilter;
import org.eclipse.hawkbit.repository.model.DistributionSetFilter.DistributionSetFilterBuilder;
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation;
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation.CancelationType;
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.MetaData;
import org.eclipse.hawkbit.repository.model.NamedEntity;
import org.eclipse.hawkbit.repository.model.NamedVersionedEntity;
import org.eclipse.hawkbit.repository.model.Rollout;
@@ -71,8 +70,6 @@ import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
/**
* {@link DistributionSetManagement} tests.
@@ -96,7 +93,7 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
assertThat(distributionSetManagement.get(NOT_EXIST_IDL)).isNotPresent();
assertThat(distributionSetManagement.getWithDetails(NOT_EXIST_IDL)).isNotPresent();
assertThat(distributionSetManagement.findByNameAndVersion(NOT_EXIST_ID, NOT_EXIST_ID)).isNotPresent();
assertThat(distributionSetManagement.findMetaDataByDistributionSetId(set.getId(), NOT_EXIST_ID)).isNotPresent();
assertThat(distributionSetManagement.getMetadata(set.getId()).get(NOT_EXIST_ID)).isNull();
}
@Test
@@ -138,44 +135,30 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
verifyThrownExceptionBy(() -> distributionSetManagement.create(
entityFactory.distributionSet().create().name("xxx").type(NOT_EXIST_ID)), "DistributionSetType");
verifyThrownExceptionBy(() -> distributionSetManagement.putMetaData(
NOT_EXIST_IDL, singletonList(entityFactory.generateDsMetadata("123", "123"))), "DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.createMetadata(NOT_EXIST_IDL, Map.of("123", "123")), "DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.delete(singletonList(NOT_EXIST_IDL)), "DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.delete(NOT_EXIST_IDL), "DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.deleteMetaData(NOT_EXIST_IDL, "xxx"),
"DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.deleteMetaData(set.getId(), NOT_EXIST_ID),
"DistributionSetMetadata");
verifyThrownExceptionBy(() -> distributionSetManagement.deleteMetadata(NOT_EXIST_IDL, "xxx"), "DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.deleteMetadata(set.getId(), NOT_EXIST_ID), "DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.findByAction(NOT_EXIST_IDL), "Action");
verifyThrownExceptionBy(() -> distributionSetManagement.findMetaDataByDistributionSetId(NOT_EXIST_IDL, "xxx"),
"DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.getMetadata(NOT_EXIST_IDL).get("xxx"), "DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.findMetaDataByDistributionSetId(NOT_EXIST_IDL, PAGE),
"DistributionSet");
verifyThrownExceptionBy(
() -> distributionSetManagement.findMetaDataByDistributionSetIdAndRsql(NOT_EXIST_IDL, "name==*", PAGE),
"DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.getMetadata(NOT_EXIST_IDL).get(PAGE), "DistributionSet");
assertThatThrownBy(() -> distributionSetManagement.isInUse(NOT_EXIST_IDL))
.isInstanceOf(EntityNotFoundException.class).hasMessageContaining(NOT_EXIST_ID)
.hasMessageContaining("DistributionSet");
verifyThrownExceptionBy(
() -> distributionSetManagement.update(entityFactory.distributionSet().update(NOT_EXIST_IDL)),
"DistributionSet");
() -> distributionSetManagement.update(entityFactory.distributionSet().update(NOT_EXIST_IDL)), "DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.updateMetaData(NOT_EXIST_IDL,
entityFactory.generateDsMetadata("xxx", "xxx")), "DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.updateMetadata(NOT_EXIST_IDL,"xxx", "xxx"), "DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.updateMetadata(set.getId(),NOT_EXIST_ID, "xxx"), "DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.updateMetaData(set.getId(),
entityFactory.generateDsMetadata(NOT_EXIST_ID, "xxx")), "DistributionSetMetadata");
verifyThrownExceptionBy(() -> distributionSetManagement.getOrElseThrowException(NOT_EXIST_IDL),
"DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.getOrElseThrowException(NOT_EXIST_IDL), "DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.getValidAndComplete(NOT_EXIST_IDL), "DistributionSet");
@@ -247,68 +230,57 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Checks that metadata for a distribution set can be created.")
void createDistributionSetMetadata() {
void createMetadata() {
final String knownKey = "dsMetaKnownKey";
final String knownValue = "dsMetaKnownValue";
final DistributionSet ds = testdataFactory.createDistributionSet("testDs");
final DistributionSetMetadata metadata = new JpaDistributionSetMetadata(knownKey, ds, knownValue);
final JpaDistributionSetMetadata createdMetadata = (JpaDistributionSetMetadata) createDistributionSetMetadata(
ds.getId(), metadata);
assertThat(createdMetadata).isNotNull();
assertThat(createdMetadata.getId().getKey()).isEqualTo(knownKey);
assertThat(createdMetadata.getDistributionSet().getId()).isEqualTo(ds.getId());
assertThat(createdMetadata.getValue()).isEqualTo(knownValue);
insertMetadata(knownKey, knownValue, ds); // and validate
}
@Test
@Description("Verifies the enforcement of the metadata quota per distribution set.")
void createDistributionSetMetadataUntilQuotaIsExceeded() {
void createMetadataUntilQuotaIsExceeded() {
// add meta data one by one
final DistributionSet ds1 = testdataFactory.createDistributionSet("ds1");
final int maxMetaData = quotaManagement.getMaxMetaDataEntriesPerDistributionSet();
for (int i = 0; i < maxMetaData; ++i) {
assertThat((JpaDistributionSetMetadata) createDistributionSetMetadata(ds1.getId(),
new JpaDistributionSetMetadata("k" + i, ds1, "v" + i))).isNotNull();
insertMetadata("k" + i, "v" + i, ds1);
}
// quota exceeded
final Long ds1Id = ds1.getId();
final JpaDistributionSetMetadata jpaMaxMetaData = new JpaDistributionSetMetadata("k" + maxMetaData, ds1, "v" + maxMetaData);
assertThatExceptionOfType(AssignmentQuotaExceededException.class)
.isThrownBy(() -> createDistributionSetMetadata(ds1Id, jpaMaxMetaData));
.isThrownBy(() -> insertMetadata("k" + maxMetaData, "v" + maxMetaData, ds1));
// add multiple meta data entries at once
final DistributionSet ds2 = testdataFactory.createDistributionSet("ds2");
final List<MetaData> metaData2 = new ArrayList<>();
final Map<String, String> metaData2 = new HashMap<>();
for (int i = 0; i < maxMetaData + 1; ++i) {
metaData2.add(new JpaDistributionSetMetadata("k" + i, ds2, "v" + i));
metaData2.put("k" + i, "v" + i);
}
// verify quota is exceeded
final Long ds2Id = ds2.getId();
assertThatExceptionOfType(AssignmentQuotaExceededException.class)
.isThrownBy(() -> createDistributionSetMetadata(ds2Id, metaData2));
.isThrownBy(() -> distributionSetManagement.createMetadata(ds2Id, metaData2));
// add some meta data entries
final DistributionSet ds3 = testdataFactory.createDistributionSet("ds3");
final int firstHalf = Math.round((maxMetaData) / 2.f);
final Long ds3Id = ds3.getId();
for (int i = 0; i < firstHalf; ++i) {
createDistributionSetMetadata(ds3Id, new JpaDistributionSetMetadata("k" + i, ds3, "v" + i));
insertMetadata("k" + i, "v" + i, ds3);
}
// add too many data entries
final int secondHalf = maxMetaData - firstHalf;
final List<MetaData> metaData3 = new ArrayList<>();
final Map<String, String> metaData3 = new HashMap<>();
for (int i = 0; i < secondHalf + 1; ++i) {
metaData3.add(new JpaDistributionSetMetadata("kk" + i, ds3, "vv" + i));
metaData3.put("kk" + i, "vv" + i);
}
// verify quota is exceeded
assertThatExceptionOfType(AssignmentQuotaExceededException.class)
.isThrownBy(() -> createDistributionSetMetadata(ds3Id, metaData3));
.isThrownBy(() -> distributionSetManagement.createMetadata(ds3Id, metaData3));
}
@Test
@@ -507,7 +479,7 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
@Test
@WithUser(allSpPermissions = true)
@Description("Checks that metadata for a distribution set can be updated.")
void updateDistributionSetMetadata() {
void updateMetadata() {
final String knownKey = "myKnownKey";
final String knownValue = "myKnownValue";
final String knownUpdateValue = "myNewUpdatedValue";
@@ -519,26 +491,22 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
waitNextMillis();
// create an DS meta data entry
createDistributionSetMetadata(ds.getId(), new JpaDistributionSetMetadata(knownKey, ds, knownValue));
insertMetadata(knownKey, knownValue, ds);
final DistributionSet changedLockRevisionDS = getOrThrow(distributionSetManagement.get(ds.getId()));
assertThat(changedLockRevisionDS.getOptLockRevision()).isEqualTo(2);
waitNextMillis();
// update the DS metadata
final JpaDistributionSetMetadata updated = (JpaDistributionSetMetadata) distributionSetManagement
.updateMetaData(ds.getId(), entityFactory.generateDsMetadata(knownKey, knownUpdateValue));
distributionSetManagement.updateMetadata(ds.getId(), knownKey, knownUpdateValue);
// we are updating the sw metadata so also modifying the base software
// module so opt lock revision must be three
final DistributionSet reloadedDS = getOrThrow(distributionSetManagement.get(ds.getId()));
assertThat(reloadedDS.getOptLockRevision()).isEqualTo(3);
assertThat(reloadedDS.getLastModifiedAt()).isPositive();
// verify updated meta data contains the updated value
assertThat(updated).isNotNull();
assertThat(updated.getValue()).isEqualTo(knownUpdateValue);
assertThat(updated.getId().getKey()).isEqualTo(knownKey);
assertThat(updated.getDistributionSet().getId()).isEqualTo(ds.getId());
// verify updated meta data is the updated value
assertThat(distributionSetManagement.getMetadata(ds.getId()).get(knownKey)).isEqualTo(knownUpdateValue);
}
@Test
@@ -783,37 +751,22 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
}
@Test
@Description("Queries and loads the metadata related to a given software module.")
void findAllDistributionSetMetadataByDsId() {
@Description("Queries and loads the metadata related to a given distribution set.")
void getMetadata() {
// create a DS
final DistributionSet ds1 = testdataFactory.createDistributionSet("testDs1");
final DistributionSet ds2 = testdataFactory.createDistributionSet("testDs2");
for (int index = 0; index < quotaManagement.getMaxMetaDataEntriesPerDistributionSet(); index++) {
createDistributionSetMetadata(ds1.getId(),
new JpaDistributionSetMetadata("key" + index, ds1, "value" + index));
insertMetadata("key" + index, "value" + index, ds1);
}
for (int index = 0; index <= quotaManagement.getMaxMetaDataEntriesPerDistributionSet() - 2; index++) {
createDistributionSetMetadata(ds2.getId(),
new JpaDistributionSetMetadata("key" + index, ds2, "value" + index));
insertMetadata("key" + index, "value" + index, ds2);
}
final Page<DistributionSetMetadata> metadataOfDs1 = distributionSetManagement
.findMetaDataByDistributionSetId(ds1.getId(), PageRequest.of(0, 100));
final Page<DistributionSetMetadata> metadataOfDs2 = distributionSetManagement
.findMetaDataByDistributionSetId(ds2.getId(), PageRequest.of(0, 100));
assertThat(metadataOfDs1.getNumberOfElements())
.isEqualTo(quotaManagement.getMaxMetaDataEntriesPerDistributionSet());
assertThat(metadataOfDs1.getTotalElements())
.isEqualTo(quotaManagement.getMaxMetaDataEntriesPerDistributionSet());
assertThat(metadataOfDs2.getNumberOfElements())
.isEqualTo(quotaManagement.getMaxMetaDataEntriesPerDistributionSet() - 1);
assertThat(metadataOfDs2.getTotalElements())
.isEqualTo(quotaManagement.getMaxMetaDataEntriesPerDistributionSet() - 1);
assertThat(distributionSetManagement.getMetadata(ds1.getId())).hasSize(quotaManagement.getMaxMetaDataEntriesPerDistributionSet());
assertThat(distributionSetManagement.getMetadata(ds2.getId())).hasSize(quotaManagement.getMaxMetaDataEntriesPerDistributionSet() - 1);
}
@Test
@@ -897,23 +850,20 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
final String knownUpdateValue = "knownUpdateValue";
final Long dsId = testdataFactory.createDistributionSet().getId();
distributionSetManagement.putMetaData(dsId,
singletonList(entityFactory.generateDsMetadata(knownKey1, knownValue)));
distributionSetManagement.createMetadata(dsId, Map.of(knownKey1, knownValue));
distributionSetInvalidationManagement.invalidateDistributionSet(
new DistributionSetInvalidation(singletonList(dsId), CancelationType.NONE, false));
// assert that no new metadata can be created
final List<MetaData> metadata = singletonList(entityFactory.generateDsMetadata(knownKey2, knownValue));
assertThatExceptionOfType(InvalidDistributionSetException.class)
.as("Invalid distributionSet should throw an exception")
.isThrownBy(() -> distributionSetManagement.putMetaData(dsId, metadata));
.isThrownBy(() -> distributionSetManagement.createMetadata(dsId, Map.of(knownKey2, knownValue)));
// assert that an existing metadata can not be updated
final MetaData metadata2 = entityFactory.generateDsMetadata(knownKey1, knownUpdateValue);
assertThatExceptionOfType(InvalidDistributionSetException.class)
.as("Invalid distributionSet should throw an exception")
.isThrownBy(() -> distributionSetManagement.updateMetaData(dsId, metadata2));
.isThrownBy(() -> distributionSetManagement.updateMetadata(dsId, knownKey1, knownUpdateValue));
}
@Test
@@ -1265,6 +1215,11 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
.isComplete(Boolean.FALSE).isDeleted(Boolean.FALSE));
}
private void insertMetadata(final String knownKey, final String knownValue, final DistributionSet distributionSet) {
distributionSetManagement.createMetadata(distributionSet.getId(), Map.of(knownKey, knownValue));
assertThat(distributionSetManagement.getMetadata(distributionSet.getId()).get(knownKey)).isEqualTo(knownValue);
}
private void assertThatFilterContainsOnlyGivenDistributionSets(final DistributionSetFilterBuilder filterBuilder,
final List<DistributionSet> distributionSets) {
final int expectedDsSize = distributionSets.size();

View File

@@ -1,234 +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 static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import java.util.Arrays;
import java.util.Collections;
import jakarta.persistence.EntityManager;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.qameta.allure.Story;
import org.eclipse.hawkbit.repository.DistributionSetFields;
import org.eclipse.hawkbit.repository.SoftwareModuleFields;
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.eclipse.hawkbit.repository.test.util.TestdataFactory;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.orm.jpa.vendor.Database;
@Feature("Component Tests - Repository")
@Story("RSQL filter distribution set")
@SuppressWarnings("java:S6813") // constructor injects are not possible for test classes
class RSQLDistributionSetFieldTest extends AbstractJpaIntegrationTest {
@Autowired
protected VirtualPropertyReplacer virtualPropertyReplacer;
@Autowired
protected EntityManager entityManager;
private DistributionSet ds;
private SoftwareModule sm;
@BeforeEach
void setupBeforeTest() {
sm = testdataFactory.createSoftwareModuleApp("SM");
ds = testdataFactory.createDistributionSet(Collections.singletonList(sm), "DS");
ds = distributionSetManagement.update(entityFactory.distributionSet().update(ds.getId()).description("DS"));
createDistributionSetMetadata(ds.getId(), entityFactory.generateDsMetadata("metaKey", "metaValue"));
DistributionSet ds2 = testdataFactory.createDistributionSets("NewDS", 3).get(0);
ds2 = distributionSetManagement.update(entityFactory.distributionSet().update(ds2.getId()).description("DS%"));
createDistributionSetMetadata(ds2.getId(), entityFactory.generateDsMetadata("metaKey", "value"));
final DistributionSetTag distSetTag = distributionSetTagManagement
.create(entityFactory.tag().create().name("Tag1"));
distributionSetTagManagement.create(entityFactory.tag().create().name("Tag2"));
distributionSetTagManagement.create(entityFactory.tag().create().name("Tag3"));
distributionSetTagManagement.create(entityFactory.tag().create().name("Tag4"));
distributionSetManagement.assignTag(Arrays.asList(ds.getId(), ds2.getId()), distSetTag.getId());
final DistributionSet ds3 = distributionSetManagement
.create(entityFactory.distributionSet().create().name("test123").version("noDescription"));
distributionSetManagement.invalidate(ds3);
}
@Test
@Description("Test filter distribution set by id")
void testFilterByParameterId() {
assertRSQLQuery(DistributionSetFields.ID.name() + "==" + ds.getId(), 1);
assertRSQLQuery(DistributionSetFields.ID.name() + "!=" + ds.getId(), 4);
assertRSQLQuery(DistributionSetFields.ID.name() + "==" + -1, 0);
assertRSQLQuery(DistributionSetFields.ID.name() + "!=" + -1, 5);
// Not supported for numbers
if (Database.POSTGRESQL.equals(getDatabase())) {
return;
}
assertRSQLQuery(DistributionSetFields.ID.name() + "=in=(" + ds.getId() + ",10000000)", 1);
assertRSQLQuery(DistributionSetFields.ID.name() + "=out=(" + ds.getId() + ",10000000)", 4);
}
@Test
@Description("Test filter distribution set by name")
void testFilterByParameterName() {
assertRSQLQuery(DistributionSetFields.NAME.name() + "==DS", 1);
assertRSQLQuery(DistributionSetFields.NAME.name() + "!=DS", 4);
assertRSQLQuery(DistributionSetFields.NAME.name() + "==*DS", 4);
assertRSQLQuery(DistributionSetFields.NAME.name() + "==noExist*", 0);
assertRSQLQuery(DistributionSetFields.NAME.name() + "=in=(DS,notexist)", 1);
assertRSQLQuery(DistributionSetFields.NAME.name() + "=out=(DS,notexist)", 4);
}
@Test
@Description("Test filter distribution set by assigned software module")
void testFilterBySoftwareModule() {
assertRSQLQuery(DistributionSetFields.MODULE.name() + "." + SoftwareModuleFields.NAME.name() + "==" + sm.getName(), 1);
assertRSQLQuery(DistributionSetFields.MODULE.name() + "." + SoftwareModuleFields.ID.name() + "==" + sm.getId(), 1);
assertRSQLQuery(DistributionSetFields.MODULE.name() + "." + SoftwareModuleFields.NAME.name() + "==noExist", 0);
assertRSQLQuery(DistributionSetFields.MODULE.name() + "." + SoftwareModuleFields.ID.name() + "=in=(" + sm.getId() + ", -1)", 1);
assertRSQLQuery(DistributionSetFields.MODULE.name() + "." + SoftwareModuleFields.ID.name() + "=out=(" + sm.getId() + ", -1)", 4);
}
@Test
@Description("Test filter distribution set by description")
void testFilterByParameterDescription() {
assertRSQLQuery(DistributionSetFields.DESCRIPTION.name() + "==''", 1);
assertRSQLQuery(DistributionSetFields.DESCRIPTION.name() + "!=''", 4);
assertRSQLQuery(DistributionSetFields.DESCRIPTION.name() + "==DS", 1);
assertRSQLQuery(DistributionSetFields.DESCRIPTION.name() + "!=DS*", 3);
assertRSQLQuery(DistributionSetFields.DESCRIPTION.name() + "!=DS", 4);
assertRSQLQuery(DistributionSetFields.DESCRIPTION.name() + "==DS*", 2);
assertRSQLQuery(DistributionSetFields.DESCRIPTION.name() + "==DS%", 1);
assertRSQLQuery(DistributionSetFields.DESCRIPTION.name() + "==noExist*", 0);
assertRSQLQuery(DistributionSetFields.DESCRIPTION.name() + "=in=(DS,notexist)", 1);
assertRSQLQuery(DistributionSetFields.DESCRIPTION.name() + "=out=(DS,notexist)", 4);
}
@Test
@Description("Test filter distribution set by version")
void testFilterByParameterVersion() {
assertRSQLQuery(DistributionSetFields.VERSION.name() + "==" + TestdataFactory.DEFAULT_VERSION, 1);
assertRSQLQuery(DistributionSetFields.VERSION.name() + "!=" + TestdataFactory.DEFAULT_VERSION, 4);
assertRSQLQuery(DistributionSetFields.VERSION.name() + "=in=(" + TestdataFactory.DEFAULT_VERSION + ",1.0.0,1.0.1)", 3);
assertRSQLQuery(DistributionSetFields.VERSION.name() + "=out=(" + TestdataFactory.DEFAULT_VERSION + ",error)", 4);
}
@Test
@Description("Test filter distribution set by complete property")
void testFilterByAttributeComplete() {
assertRSQLQuery(DistributionSetFields.COMPLETE.name() + "==true", 3);
final String noExistStar = DistributionSetFields.COMPLETE.name() + "==noExist*";
assertThatExceptionOfType(RSQLParameterSyntaxException.class)
.isThrownBy(() -> assertRSQLQuery(noExistStar, 0));
assertRSQLQuery(DistributionSetFields.COMPLETE.name() + "=in=(true)", 3);
assertRSQLQuery(DistributionSetFields.COMPLETE.name() + "=out=(true)", 2);
}
@Test
@Description("Test filter distribution set by valid property")
void testFilterByAttributeValid() {
assertRSQLQuery(DistributionSetFields.VALID.name() + "==true", 4);
assertRSQLQuery(DistributionSetFields.VALID.name() + "==false", 1);
final String rsqlNoExistStar = DistributionSetFields.VALID.name() + "==noExist*";
assertThatExceptionOfType(RSQLParameterSyntaxException.class)
.isThrownBy(() -> assertRSQLQuery(rsqlNoExistStar, 0));
assertRSQLQuery(DistributionSetFields.VALID.name() + "=in=(true)", 4);
assertRSQLQuery(DistributionSetFields.VALID.name() + "=out=(true)", 1);
}
@Test
@Description("Test filter distribution set by tag name")
void testFilterByTag() {
assertRSQLQuery(DistributionSetFields.TAG.name() + "==Tag1", 2);
assertRSQLQuery(DistributionSetFields.TAG.name() + "!=Tag1", 3);
assertRSQLQuery(DistributionSetFields.TAG.name() + "==T*", 2);
assertRSQLQuery(DistributionSetFields.TAG.name() + "==noExist*", 0);
assertRSQLQuery(DistributionSetFields.TAG.name() + "=in=(Tag1,notexist)", 2);
assertRSQLQuery(DistributionSetFields.TAG.name() + "=out=(null)", 5);
}
@Test
@Description("Test filter distribution set by type key")
void testFilterByType() {
assertRSQLQuery(DistributionSetFields.TYPE.name() + "==" + TestdataFactory.DS_TYPE_DEFAULT, 4);
assertRSQLQuery(DistributionSetFields.TYPE.name() + "==noExist*", 0);
assertRSQLQuery(DistributionSetFields.TYPE.name() + "=in=(" + TestdataFactory.DS_TYPE_DEFAULT + ",ecl)", 4);
assertRSQLQuery(DistributionSetFields.TYPE.name() + "=out=(" + TestdataFactory.DS_TYPE_DEFAULT + ")", 1);
}
@Test
@Description("Test filter distribution set by metadata")
void testFilterByMetadata() {
createDistributionSetWithMetadata("key.dot", "value.dot");
assertRSQLQuery(DistributionSetFields.METADATA.name() + ".metaKey==metaValue", 1);
assertRSQLQuery(DistributionSetFields.METADATA.name() + ".metaKey==*v*", 2);
assertRSQLQuery(DistributionSetFields.METADATA.name() + ".metaKey==noExist*", 0);
assertRSQLQuery(DistributionSetFields.METADATA.name() + ".metaKey=in=(metaValue,notexist)", 1);
assertRSQLQuery(DistributionSetFields.METADATA.name() + ".metaKey=out=(metaValue,notexist)", 1);
assertRSQLQuery(DistributionSetFields.METADATA.name() + ".notExist==metaValue", 0);
assertRSQLQuery(DistributionSetFields.METADATA.name() + ".key.dot==value.dot", 1);
assertRSQLQuery(DistributionSetFields.METADATA.name() + ".key.dot*==value.dot", 0);
assertRSQLQuery(DistributionSetFields.METADATA.name() + ".key.*==value.dot", 0);
assertRSQLQuery(DistributionSetFields.METADATA.name() + ".key.==value.dot", 0);
assertRSQLQuery(DistributionSetFields.METADATA.name() + ".key*==value.dot", 0);
assertRSQLQuery(DistributionSetFields.METADATA.name() + ".*==value.dot", 0);
assertRSQLQuery(DistributionSetFields.METADATA.name() + "..==value.dot", 0);
assertRSQLQueryThrowsException(
DistributionSetFields.METADATA.name() + ".==value.dot",
RSQLParameterUnsupportedFieldException.class);
assertRSQLQueryThrowsException(
DistributionSetFields.METADATA.name() + "*==value.dot",
RSQLParameterUnsupportedFieldException.class);
assertRSQLQueryThrowsException(
DistributionSetFields.METADATA.name() + "==value.dot",
RSQLParameterUnsupportedFieldException.class);
}
private void assertRSQLQuery(final String rsqlParam, final long expectedEntity) {
final Page<DistributionSet> find = distributionSetManagement.findByRsql(rsqlParam, PageRequest.of(0, 100));
final long countAll = find.getTotalElements();
assertThat(find).as("Found entity is should not be null").isNotNull();
assertThat(countAll).as("Found entity size is wrong").isEqualTo(expectedEntity);
}
private <T extends Throwable> void assertRSQLQueryThrowsException(final String rsqlParam, final Class<T> expectedException) {
assertThatExceptionOfType(expectedException)
.isThrownBy(() -> RSQLUtility.validateRsqlFor(
rsqlParam, DistributionSetFields.class, JpaDistributionSet.class, virtualPropertyReplacer, entityManager));
}
private DistributionSet createDistributionSetWithMetadata(final String metadataKeyName, final String metadataValue) {
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
createDistributionSetMetadata(distributionSet.getId(), entityFactory.generateDsMetadata(metadataKeyName, metadataValue));
return distributionSet;
}
}

View File

@@ -1,81 +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.Arrays;
import java.util.List;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.qameta.allure.Story;
import org.eclipse.hawkbit.repository.DistributionSetMetadataFields;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.MetaData;
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 distribution set metadata")
class RSQLDistributionSetMetadataFieldsTest extends AbstractJpaIntegrationTest {
private Long distributionSetId;
@BeforeEach
void setupBeforeTest() {
final DistributionSet distributionSet = testdataFactory.createDistributionSet("DS");
distributionSetId = distributionSet.getId();
final List<MetaData> metadata = new ArrayList<>(5);
for (int i = 0; i < 5; i++) {
metadata.add(entityFactory.generateDsMetadata("" + i, "" + i));
}
distributionSetManagement.putMetaData(distributionSetId, metadata);
distributionSetManagement.putMetaData(distributionSetId,
Arrays.asList(entityFactory.generateDsMetadata("emptyValueTest", null)));
}
@Test
@Description("Test filter distribution set metadata by key")
void testFilterByParameterKey() {
assertRSQLQuery(DistributionSetMetadataFields.KEY.name() + "==1", 1);
assertRSQLQuery(DistributionSetMetadataFields.KEY.name() + "!=1", 5);
assertRSQLQuery(DistributionSetMetadataFields.KEY.name() + "=in=(1,2)", 2);
assertRSQLQuery(DistributionSetMetadataFields.KEY.name() + "=out=(1,2)", 4);
}
@Test
@Description("Test filter distribution set metadata by value")
void testFilterByParameterValue() {
assertRSQLQuery(DistributionSetMetadataFields.VALUE.name() + "==''", 1);
assertRSQLQuery(DistributionSetMetadataFields.VALUE.name() + "!=''", 5);
assertRSQLQuery(DistributionSetMetadataFields.VALUE.name() + "==1", 1);
assertRSQLQuery(DistributionSetMetadataFields.VALUE.name() + "!=1", 5);
assertRSQLQuery(DistributionSetMetadataFields.VALUE.name() + "=in=(1,2)", 2);
assertRSQLQuery(DistributionSetMetadataFields.VALUE.name() + "=out=(1,2)", 4);
}
private void assertRSQLQuery(final String rsqlParam, final long expectedEntities) {
final Page<DistributionSetMetadata> findEnitity = distributionSetManagement
.findMetaDataByDistributionSetIdAndRsql(distributionSetId, rsqlParam, PageRequest.of(0, 100));
final long countAllEntities = findEnitity.getTotalElements();
assertThat(findEnitity).isNotNull();
assertThat(countAllEntities).isEqualTo(expectedEntities);
}
}

View File

@@ -21,6 +21,7 @@ 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;
@@ -56,7 +57,6 @@ import org.eclipse.hawkbit.repository.model.Action.ActionType;
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.DistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.MetaData;
import org.eclipse.hawkbit.repository.model.RepositoryModelConstants;
@@ -412,16 +412,7 @@ public abstract class AbstractIntegrationTest {
}
protected boolean isConfirmationFlowActive() {
return tenantConfigurationManagement.getConfigurationValue(TenantConfigurationKey.USER_CONFIRMATION_ENABLED,
Boolean.class).getValue();
}
protected DistributionSetMetadata createDistributionSetMetadata(final long dsId, final MetaData md) {
return createDistributionSetMetadata(dsId, Collections.singletonList(md)).get(0);
}
protected List<DistributionSetMetadata> createDistributionSetMetadata(final long dsId, final List<MetaData> md) {
return distributionSetManagement.putMetaData(dsId, md);
return tenantConfigurationManagement.getConfigurationValue(TenantConfigurationKey.USER_CONFIRMATION_ENABLED, Boolean.class).getValue();
}
protected Long getOsModule(final DistributionSet ds) {