Soft Module metadata as complex map value (#2568)
--------- Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -32,7 +32,6 @@ import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
||||
import org.eclipse.hawkbit.repository.model.AutoConfirmationStatus;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.ControllerPollProperties;
|
||||
@@ -69,14 +68,13 @@ public interface ControllerManagement {
|
||||
Optional<SoftwareModule> getSoftwareModule(long moduleId);
|
||||
|
||||
/**
|
||||
* Retrieves {@link SoftwareModuleMetadata} where {@link SoftwareModuleMetadata#isTargetVisible()}.
|
||||
* Retrieves software module metadata where isTargetVisible.
|
||||
*
|
||||
* @param moduleId of the {@link SoftwareModule}
|
||||
* @return the map of software module id to {@link SoftwareModuleMetadata} with maximum size of
|
||||
* {@link RepositoryConstants#MAX_META_DATA_COUNT}
|
||||
* @return the map of software module id to software module metadata with maximum size of {@link RepositoryConstants#MAX_META_DATA_COUNT}
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
|
||||
Map<Long, List<SoftwareModuleMetadata>> findTargetVisibleMetaDataBySoftwareModuleId(@NotNull Collection<Long> moduleId);
|
||||
Map<Long, Map<String, String>> findTargetVisibleMetaDataBySoftwareModuleId(@NotNull Collection<Long> moduleId);
|
||||
|
||||
/**
|
||||
* Simple addition of a new {@link ActionStatus} entry to the {@link Action}. No state changes.
|
||||
|
||||
@@ -12,7 +12,6 @@ package org.eclipse.hawkbit.repository;
|
||||
import org.eclipse.hawkbit.repository.builder.ActionStatusBuilder;
|
||||
import org.eclipse.hawkbit.repository.builder.RolloutBuilder;
|
||||
import org.eclipse.hawkbit.repository.builder.RolloutGroupBuilder;
|
||||
import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataBuilder;
|
||||
import org.eclipse.hawkbit.repository.builder.TagBuilder;
|
||||
import org.eclipse.hawkbit.repository.builder.TargetBuilder;
|
||||
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryBuilder;
|
||||
@@ -31,11 +30,6 @@ public interface EntityFactory {
|
||||
*/
|
||||
ActionStatusBuilder actionStatus();
|
||||
|
||||
/**
|
||||
* @return {@link SoftwareModuleMetadataBuilder} object
|
||||
*/
|
||||
SoftwareModuleMetadataBuilder softwareModuleMetadata();
|
||||
|
||||
/**
|
||||
* @return {@link TagBuilder} object
|
||||
*/
|
||||
|
||||
@@ -9,14 +9,17 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
|
||||
/**
|
||||
* Repository constants.
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public final class RepositoryConstants {
|
||||
|
||||
/**
|
||||
@@ -46,12 +49,7 @@ public final class RepositoryConstants {
|
||||
/**
|
||||
* Maximum number of metadata entries provided to controllers.
|
||||
*
|
||||
* @see SoftwareModuleMetadata#isTargetVisible()
|
||||
* @see SoftwareModule.MetadataValueCreate#isTargetVisible()
|
||||
*/
|
||||
public static final int MAX_META_DATA_COUNT = 50;
|
||||
|
||||
private RepositoryConstants() {
|
||||
// Utility class.
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@
|
||||
package org.eclipse.hawkbit.repository;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -25,8 +24,6 @@ import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.eclipse.hawkbit.im.authentication.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataCreate;
|
||||
import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataUpdate;
|
||||
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
@@ -34,9 +31,8 @@ import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.NamedEntity;
|
||||
import org.eclipse.hawkbit.repository.model.NamedVersionedEntity;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule.MetadataValue;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||
import org.eclipse.hawkbit.repository.model.Type;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
@@ -53,27 +49,27 @@ public interface SoftwareModuleManagement<T extends SoftwareModule>
|
||||
return "SOFTWARE_MODULE";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates or updates a distribution set meta-data value.
|
||||
*
|
||||
* @param id the software module id which meta-data has to be updated
|
||||
* @param key the key of the meta-data entry to be updated
|
||||
* @param value the meta-data value to be updated
|
||||
* @throws EntityNotFoundException in case the meta-data entry does not exist and cannot be updated
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_UPDATE_REPOSITORY)
|
||||
void createMetadata(@NotNull Long id, @NotEmpty String key, @NotNull @Valid MetadataValue value);
|
||||
|
||||
/**
|
||||
* Creates a list of software module meta-data entries.
|
||||
*
|
||||
* @param metadata the meta-data entries to create
|
||||
* @throws EntityAlreadyExistsException in case one of the meta-data entry already exists for the specific key
|
||||
* @throws EntityNotFoundException if software module with given ID does not exist
|
||||
* @throws AssignmentQuotaExceededException if the maximum number of {@link SoftwareModuleMetadata} entries is exceeded for the addressed
|
||||
* {@link SoftwareModule}
|
||||
* @throws AssignmentQuotaExceededException if the maximum number of meta-data entries is exceeded for the addressed {@link SoftwareModule}
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_UPDATE_REPOSITORY)
|
||||
void createMetadata(@NotNull @Valid Collection<SoftwareModuleMetadataCreate> metadata);
|
||||
|
||||
/**
|
||||
* Finds all meta-data by the given software module id.
|
||||
*
|
||||
* @param id the software module id to retrieve the meta-data from
|
||||
* @return a paged result of all meta-data entries for a given software module id
|
||||
* @throws EntityNotFoundException if software module with given ID does not exist
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY)
|
||||
List<SoftwareModuleMetadata> getMetadata(long id);
|
||||
void createMetadata(@NotNull Long id, @NotEmpty @Valid Map<String, ? extends MetadataValue> metadata);
|
||||
|
||||
/**
|
||||
* Finds all meta-data by the given software module id and key.
|
||||
@@ -84,52 +80,30 @@ public interface SoftwareModuleManagement<T extends SoftwareModule>
|
||||
* @throws EntityNotFoundException if software module with given ID does not exist ot the
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY)
|
||||
SoftwareModuleMetadata getMetadata(long id, String key);
|
||||
MetadataValue getMetadata(@NotNull Long id, @NotEmpty String key);
|
||||
|
||||
/**
|
||||
* Finds all meta-data by the given software module id where {@link SoftwareModuleMetadata#isTargetVisible()}.
|
||||
* Finds all meta-data by the given software module id.
|
||||
*
|
||||
* @param id the software module 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 software module id
|
||||
* @throws EntityNotFoundException if software module with given ID does not exist
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY)
|
||||
Page<SoftwareModuleMetadata> findMetaDataBySoftwareModuleIdAndTargetVisible(long id, @NotNull Pageable pageable);
|
||||
|
||||
/**
|
||||
* Creates or updates a single software module meta-data entry.
|
||||
*
|
||||
* @param metadata the meta-data entry to create
|
||||
* @return the updated or created software module meta-data entry
|
||||
* @throws EntityAlreadyExistsException in case the meta-data entry already exists for the specific key
|
||||
* @throws EntityNotFoundException if software module with given ID does not exist
|
||||
* @throws AssignmentQuotaExceededException if the maximum number of {@link SoftwareModuleMetadata}
|
||||
* entries is exceeded for the addressed {@link SoftwareModule}
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_UPDATE_REPOSITORY)
|
||||
SoftwareModuleMetadata updateMetadata(@NotNull @Valid SoftwareModuleMetadataCreate metadata);
|
||||
|
||||
/**
|
||||
* Updates a distribution set meta-data value if corresponding entry exists.
|
||||
*
|
||||
* @param update the meta-data entry to be updated
|
||||
* @return the updated meta-data entry
|
||||
* @throws EntityNotFoundException in case the meta-data entry does not exist and cannot be updated
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_UPDATE_REPOSITORY)
|
||||
SoftwareModuleMetadata updateMetadata(@NotNull @Valid SoftwareModuleMetadataUpdate update);
|
||||
Map<String, MetadataValue> getMetadata(@NotNull Long id);
|
||||
|
||||
/**
|
||||
* Deletes a software module meta-data entry.
|
||||
*
|
||||
* @param id where meta-data has to be deleted
|
||||
* @param key of the meta-data element
|
||||
* @return true if really deleted, false if not
|
||||
* @throws EntityNotFoundException if software module with given ID does not exist or the key is not found
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_UPDATE_REPOSITORY)
|
||||
void deleteMetadata(long id, @NotEmpty String key);
|
||||
void deleteMetadata(@NotNull Long id, @NotEmpty String key);
|
||||
|
||||
@PreAuthorize(SpringEvalExpressions.IS_SYSTEM_CODE)
|
||||
Map<Long, Map<String, String>> findMetaDataBySoftwareModuleIdsAndTargetVisible(Collection<Long> ids);
|
||||
|
||||
/**
|
||||
* Locks a software module.
|
||||
@@ -198,9 +172,6 @@ public interface SoftwareModuleManagement<T extends SoftwareModule>
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY)
|
||||
Slice<T> findByType(long typeId, @NotNull Pageable pageable);
|
||||
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY)
|
||||
Map<Long, List<SoftwareModuleMetadata>> findMetaDataBySoftwareModuleIdsAndTargetVisible(Collection<Long> moduleIds);
|
||||
|
||||
/**
|
||||
* Returns count of all modules assigned to given {@link DistributionSet}.
|
||||
*
|
||||
|
||||
@@ -1,32 +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.builder;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
|
||||
|
||||
/**
|
||||
* Builder for {@link SoftwareModuleMetadata}.
|
||||
*/
|
||||
public interface SoftwareModuleMetadataBuilder {
|
||||
|
||||
/**
|
||||
* @param softwareModuleId of the {@link SoftwareModule} the {@link SoftwareModuleMetadata} belongs to
|
||||
* @param key of {@link SoftwareModuleMetadata#getKey()}
|
||||
* @return builder instance
|
||||
*/
|
||||
SoftwareModuleMetadataUpdate update(long softwareModuleId, String key);
|
||||
|
||||
/**
|
||||
* @param softwareModuleId of the {@link SoftwareModule} the {@link SoftwareModuleMetadata} belongs to
|
||||
* @return builder instance
|
||||
*/
|
||||
SoftwareModuleMetadataCreate create(long softwareModuleId);
|
||||
}
|
||||
@@ -1,49 +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.builder;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.BaseEntity;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
|
||||
|
||||
/**
|
||||
* Builder to create a new {@link SoftwareModuleMetadata} entry. Defines all
|
||||
* fields that can be set at creation time. Other fields are set by the
|
||||
* repository automatically, e.g. {@link BaseEntity#getCreatedAt()}.
|
||||
*/
|
||||
public interface SoftwareModuleMetadataCreate {
|
||||
|
||||
/**
|
||||
* @param key for {@link SoftwareModuleMetadata#getKey()}
|
||||
* @return updated builder instance
|
||||
*/
|
||||
SoftwareModuleMetadataCreate key(@Size(min = 1, max =SoftwareModule.METADATA_KEY_MAX_SIZE) @NotNull String key);
|
||||
|
||||
/**
|
||||
* @param value for {@link SoftwareModuleMetadata#getValue()}
|
||||
* @return updated builder instance
|
||||
*/
|
||||
SoftwareModuleMetadataCreate value(@Size(max = SoftwareModule.METADATA_VALUE_MAX_SIZE) String value);
|
||||
|
||||
/**
|
||||
* @param visible for {@link SoftwareModuleMetadata#isTargetVisible()}
|
||||
* @return updated builder instance
|
||||
*/
|
||||
SoftwareModuleMetadataCreate targetVisible(Boolean visible);
|
||||
|
||||
/**
|
||||
* @return peek on current state of {@link SoftwareModuleMetadata} in the
|
||||
* builder
|
||||
*/
|
||||
SoftwareModuleMetadata build();
|
||||
}
|
||||
@@ -1,35 +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.builder;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
|
||||
|
||||
/**
|
||||
* Builder to update an existing {@link SoftwareModuleMetadata} entry. Defines
|
||||
* all fields that can be updated.
|
||||
*/
|
||||
public interface SoftwareModuleMetadataUpdate {
|
||||
|
||||
/**
|
||||
* @param value for {@link SoftwareModuleMetadata#getValue()}
|
||||
* @return updated builder instance
|
||||
*/
|
||||
SoftwareModuleMetadataUpdate value(@Size(min = 1, max = SoftwareModule.METADATA_VALUE_MAX_SIZE) @NotNull String value);
|
||||
|
||||
/**
|
||||
* @param visible for {@link SoftwareModuleMetadata#isTargetVisible()}
|
||||
* @return updated builder instance
|
||||
*/
|
||||
SoftwareModuleMetadataUpdate targetVisible(Boolean visible);
|
||||
}
|
||||
@@ -12,6 +12,11 @@ package org.eclipse.hawkbit.repository.model;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Software package as sub element of a {@link DistributionSet}.
|
||||
*/
|
||||
@@ -81,4 +86,28 @@ public interface SoftwareModule extends NamedVersionedEntity {
|
||||
return getArtifacts().stream().filter(artifact -> artifact.getFilename().equalsIgnoreCase(fileName.trim()))
|
||||
.findAny();
|
||||
}
|
||||
|
||||
interface MetadataValue {
|
||||
|
||||
String getValue();
|
||||
boolean isTargetVisible();
|
||||
}
|
||||
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
class MetadataValueCreate implements MetadataValue {
|
||||
|
||||
@Size(max = METADATA_VALUE_MAX_SIZE)
|
||||
private String value;
|
||||
private boolean targetVisible;
|
||||
|
||||
public MetadataValueCreate(final String value) {
|
||||
this(value, false);
|
||||
}
|
||||
|
||||
public MetadataValueCreate(final String value, final boolean targetVisible) {
|
||||
this.value = value;
|
||||
this.targetVisible = targetVisible;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Metadata element of a {@link SoftwareModule}. The software module metadata is not only (key, value) pair (like the metadata of
|
||||
* targets and distribution sets), but also contains the information if the metadata is visible for targets as part of {@link Action}.
|
||||
*/
|
||||
public interface SoftwareModuleMetadata extends Serializable {
|
||||
|
||||
/**
|
||||
* @return the key
|
||||
*/
|
||||
String getKey();
|
||||
|
||||
/**
|
||||
* @return the value
|
||||
*/
|
||||
String getValue();
|
||||
|
||||
/**
|
||||
* @return <code>true</code> if element is visible for targets as part of {@link Action}.
|
||||
*/
|
||||
boolean isTargetVisible();
|
||||
}
|
||||
@@ -1,24 +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.builder;
|
||||
|
||||
/**
|
||||
* Update implementation.
|
||||
*/
|
||||
public class GenericSoftwareModuleMetadataUpdate
|
||||
extends AbstractSoftwareModuleMetadataUpdateCreate<SoftwareModuleMetadataUpdate>
|
||||
implements SoftwareModuleMetadataUpdate {
|
||||
|
||||
public GenericSoftwareModuleMetadataUpdate(final long softwareModuleId, final String key) {
|
||||
super.softwareModuleId = softwareModuleId;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -149,17 +149,16 @@ public class SpecificationBuilder<T> {
|
||||
if (comparison.getValue() == null) {
|
||||
// map entry with key is null (doesn't exist) / is not null (exists) - use left join with on
|
||||
return switch (op) {
|
||||
case EQ -> cb.isNull(pathResolver.getJoinOn(attribute, split[1]));
|
||||
case NE -> cb.isNotNull(pathResolver.getJoinOn(attribute, split[1]));
|
||||
case EQ -> cb.isNull(toMapValuePath(pathResolver.getJoinOn(attribute, split[1])));
|
||||
case NE -> cb.isNotNull(toMapValuePath(pathResolver.getJoinOn(attribute, split[1])));
|
||||
default -> throw new RSQLParameterSyntaxException(
|
||||
String.format("Operator %s is not supported for map fields with value null", op));
|
||||
};
|
||||
} else {
|
||||
final MapJoin<?, ?, ?> mapPath = (MapJoin<?, ?, ?>) pathResolver.getPath(attribute);
|
||||
final Path<String> valuePath = (Path<String>) mapPath.value();
|
||||
return isNot(op)
|
||||
? compare(comparison, pathResolver.getJoinOnInner(attribute, split[1]))
|
||||
: cb.and(equal(mapPath.key(), split[1]), compare(comparison, valuePath));
|
||||
? compare(comparison, toMapValuePath(pathResolver.getJoinOnInner(attribute, split[1])))
|
||||
: cb.and(equal(mapPath.key(), split[1]), compare(comparison, toMapValuePath(mapPath)));
|
||||
}
|
||||
} else if (attribute instanceof SetAttribute<?, ?> setAttribute) {
|
||||
if (split.length < 2 || ObjectUtils.isEmpty(split[1])) {
|
||||
@@ -185,6 +184,12 @@ public class SpecificationBuilder<T> {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Path<String> toMapValuePath(final Path<?> mapJoin) {
|
||||
final Path<?> valuePath = ((MapJoin<?, ?, ?>) mapJoin).value();
|
||||
return valuePath.getJavaType() == String.class ? (Path<String>) valuePath : valuePath.get("value");
|
||||
}
|
||||
|
||||
private Predicate compare(final Comparison comparison, final Path<?> fieldPath) {
|
||||
final List<Object> values = getValues(comparison, fieldPath.getJavaType());
|
||||
final Object firstValue = values.get(0);
|
||||
|
||||
@@ -343,8 +343,7 @@ public class JpaQueryRsqlVisitor<A extends Enum<A> & RsqlQueryField, T> extends
|
||||
|
||||
final Predicate mapPredicate = mapToMapPredicate(fieldPath, queryPath);
|
||||
|
||||
|
||||
final Predicate valuePredicate = addOperatorPredicate(node, fieldPath, transformedValues, value, queryPath);
|
||||
final Predicate valuePredicate = addOperatorPredicate(node, queryPath.getEnumValue().isMap() ? (Path)toMapValuePath(fieldPath) : fieldPath, transformedValues, value, queryPath);
|
||||
return toSingleList(mapPredicate != null ? cb.and(mapPredicate, valuePredicate) : valuePredicate);
|
||||
}
|
||||
|
||||
@@ -380,6 +379,16 @@ public class JpaQueryRsqlVisitor<A extends Enum<A> & RsqlQueryField, T> extends
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Path<String> toMapValuePath(final Path<?> fieldPath) {
|
||||
final Path<?> mapValuePath = ((MapJoin<?, ?, ?>) pathOfString(fieldPath)).value();
|
||||
if (mapValuePath.getJavaType() == String.class) {
|
||||
return (Path<String>) mapValuePath;
|
||||
} else {
|
||||
return mapValuePath.get("value");
|
||||
}
|
||||
}
|
||||
|
||||
private Predicate getOutPredicate(
|
||||
final List<Object> transformedValues,
|
||||
final QueryPath queryPath, final Path<Object> fieldPath) {
|
||||
@@ -496,8 +505,7 @@ public class JpaQueryRsqlVisitor<A extends Enum<A> & RsqlQueryField, T> extends
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private Expression<String> getExpressionToCompare(final Path innerFieldPath, final A enumField) {
|
||||
if (enumField.isMap()){
|
||||
// Currently we support only string key. So below cast is safe.
|
||||
return (Expression<String>) (((MapJoin<?, ?, ?>) pathOfString(innerFieldPath)).value());
|
||||
return toMapValuePath(innerFieldPath);
|
||||
} else {
|
||||
return pathOfString(innerFieldPath);
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ public class JpaQueryRsqlVisitorG2<A extends Enum<A> & RsqlQueryField, T>
|
||||
if (values.get(0) == null) {
|
||||
// IS operator for maps and null value is treated as doesn't exist correspondingly
|
||||
((PluralJoin<?, ?, ?>) fieldPath).on(toMapEntryKeyPredicate(queryPath, fieldPath));
|
||||
return cb.isNull(fieldPath);
|
||||
return cb.isNull(toMapValuePath(fieldPath));
|
||||
}
|
||||
} else if (node.getOperator() == NOT) {
|
||||
if (values.size() != 1) {
|
||||
@@ -139,10 +139,10 @@ public class JpaQueryRsqlVisitorG2<A extends Enum<A> & RsqlQueryField, T>
|
||||
((PluralJoin<?, ?, ?>) fieldPath).on(toMapEntryKeyPredicate(queryPath, fieldPath));
|
||||
if (values.get(0) == null) {
|
||||
// special handling of "exists"
|
||||
return cb.isNotNull(fieldPath);
|
||||
return cb.isNotNull(toMapValuePath(fieldPath));
|
||||
} else {
|
||||
// special handling or "not equal" or null (same as != but with possible optimized join - no subquery)
|
||||
return toNotEqualToPredicate(queryPath, fieldPath, values.get(0));
|
||||
return toNotEqualToPredicate(queryPath, toMapValuePath(fieldPath), values.get(0));
|
||||
}
|
||||
}
|
||||
mapEntryKeyPredicate = toMapEntryKeyPredicate(queryPath, fieldPath);
|
||||
@@ -150,11 +150,21 @@ public class JpaQueryRsqlVisitorG2<A extends Enum<A> & RsqlQueryField, T>
|
||||
mapEntryKeyPredicate = null;
|
||||
}
|
||||
|
||||
final Predicate valuePredicate = toOperatorAndValuePredicate(node, queryPath, fieldPath, values);
|
||||
final Predicate valuePredicate = toOperatorAndValuePredicate(node, queryPath, queryPath.getEnumValue().isMap() ? toMapValuePath(fieldPath) : fieldPath, values);
|
||||
|
||||
return mapEntryKeyPredicate == null ? valuePredicate : cb.and(mapEntryKeyPredicate, valuePredicate);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Path<String> toMapValuePath(final Path<?> fieldPath) {
|
||||
final Path<?> mapValuePath = ((MapJoin<?, ?, ?>) pathOfString(fieldPath)).value();
|
||||
if (mapValuePath.getJavaType() == String.class) {
|
||||
return (Path<String>) mapValuePath;
|
||||
} else {
|
||||
return mapValuePath.get("value");
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Predicate toMapEntryKeyPredicate(final QueryPath queryPath, final Path<?> fieldPath) {
|
||||
final String[] graph = queryPath.getJpaPath();
|
||||
@@ -341,7 +351,7 @@ public class JpaQueryRsqlVisitorG2<A extends Enum<A> & RsqlQueryField, T>
|
||||
private Path<String> getExpressionToCompare(final A enumField, final Path fieldPath) {
|
||||
if (enumField.isMap()) {
|
||||
// Currently we support only string key. So below cast is safe.
|
||||
return (Path<String>) (((MapJoin<?, ?, ?>) fieldPath).value());
|
||||
return toMapValuePath(fieldPath);
|
||||
} else {
|
||||
return pathOfString(fieldPath);
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ import org.eclipse.hawkbit.repository.EntityFactory;
|
||||
import org.eclipse.hawkbit.repository.builder.ActionStatusBuilder;
|
||||
import org.eclipse.hawkbit.repository.builder.RolloutBuilder;
|
||||
import org.eclipse.hawkbit.repository.builder.RolloutGroupBuilder;
|
||||
import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataBuilder;
|
||||
import org.eclipse.hawkbit.repository.builder.TagBuilder;
|
||||
import org.eclipse.hawkbit.repository.builder.TargetBuilder;
|
||||
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryBuilder;
|
||||
@@ -33,19 +32,16 @@ public class JpaEntityFactory implements EntityFactory {
|
||||
private final TargetBuilder targetBuilder;
|
||||
private final TargetTypeBuilder targetTypeBuilder;
|
||||
private final TargetFilterQueryBuilder targetFilterQueryBuilder;
|
||||
private final SoftwareModuleMetadataBuilder softwareModuleMetadataBuilder;
|
||||
private final RolloutBuilder rolloutBuilder;
|
||||
|
||||
@SuppressWarnings("java:S107")
|
||||
public JpaEntityFactory(
|
||||
final TargetBuilder targetBuilder, final TargetTypeBuilder targetTypeBuilder,
|
||||
final TargetFilterQueryBuilder targetFilterQueryBuilder,
|
||||
final SoftwareModuleMetadataBuilder softwareModuleMetadataBuilder,
|
||||
final RolloutBuilder rolloutBuilder) {
|
||||
this.targetBuilder = targetBuilder;
|
||||
this.targetTypeBuilder = targetTypeBuilder;
|
||||
this.targetFilterQueryBuilder = targetFilterQueryBuilder;
|
||||
this.softwareModuleMetadataBuilder = softwareModuleMetadataBuilder;
|
||||
this.rolloutBuilder = rolloutBuilder;
|
||||
}
|
||||
@Override
|
||||
@@ -53,11 +49,6 @@ public class JpaEntityFactory implements EntityFactory {
|
||||
return new JpaActionStatusBuilder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SoftwareModuleMetadataBuilder softwareModuleMetadata() {
|
||||
return softwareModuleMetadataBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TagBuilder<Tag> tag() {
|
||||
return (TagBuilder)new JpaTagBuilder();
|
||||
|
||||
@@ -44,7 +44,6 @@ import org.eclipse.hawkbit.repository.artifact.encryption.ArtifactEncryptionSecr
|
||||
import org.eclipse.hawkbit.repository.artifact.encryption.ArtifactEncryptionService;
|
||||
import org.eclipse.hawkbit.repository.autoassign.AutoAssignExecutor;
|
||||
import org.eclipse.hawkbit.repository.builder.RolloutBuilder;
|
||||
import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataBuilder;
|
||||
import org.eclipse.hawkbit.repository.builder.TargetBuilder;
|
||||
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryBuilder;
|
||||
import org.eclipse.hawkbit.repository.builder.TargetTypeBuilder;
|
||||
@@ -60,7 +59,6 @@ import org.eclipse.hawkbit.repository.jpa.autocleanup.AutoActionCleanup;
|
||||
import org.eclipse.hawkbit.repository.jpa.autocleanup.AutoCleanupScheduler;
|
||||
import org.eclipse.hawkbit.repository.jpa.autocleanup.CleanupTask;
|
||||
import org.eclipse.hawkbit.repository.jpa.builder.JpaRolloutBuilder;
|
||||
import org.eclipse.hawkbit.repository.jpa.builder.JpaSoftwareModuleMetadataBuilder;
|
||||
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetBuilder;
|
||||
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetFilterQueryBuilder;
|
||||
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetTypeBuilder;
|
||||
@@ -70,7 +68,6 @@ import org.eclipse.hawkbit.repository.jpa.event.JpaEventEntityManager;
|
||||
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitDefaultServiceExecutor;
|
||||
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
|
||||
import org.eclipse.hawkbit.repository.jpa.utils.ExceptionMapper;
|
||||
import org.eclipse.hawkbit.repository.jpa.management.JpaSoftwareModuleManagement;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaArtifact;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
||||
@@ -323,12 +320,6 @@ public class JpaRepositoryConfiguration {
|
||||
return new JpaTargetTypeBuilder(dsTypeManagement);
|
||||
}
|
||||
|
||||
@Bean
|
||||
SoftwareModuleMetadataBuilder softwareModuleMetadataBuilder(
|
||||
final JpaSoftwareModuleManagement softwareModuleManagement) {
|
||||
return new JpaSoftwareModuleMetadataBuilder(softwareModuleManagement);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param distributionSetManagement for loading {@link Rollout#getDistributionSet()}
|
||||
* @return RolloutBuilder bean
|
||||
@@ -461,11 +452,9 @@ public class JpaRepositoryConfiguration {
|
||||
EntityFactory entityFactory(
|
||||
final TargetBuilder targetBuilder, final TargetTypeBuilder targetTypeBuilder,
|
||||
final TargetFilterQueryBuilder targetFilterQueryBuilder,
|
||||
final SoftwareModuleMetadataBuilder softwareModuleMetadataBuilder,
|
||||
final RolloutBuilder rolloutBuilder) {
|
||||
return new JpaEntityFactory(
|
||||
targetBuilder, targetTypeBuilder, targetFilterQueryBuilder,
|
||||
softwareModuleMetadataBuilder, rolloutBuilder);
|
||||
targetBuilder, targetTypeBuilder, targetFilterQueryBuilder, rolloutBuilder);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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.builder;
|
||||
|
||||
import org.eclipse.hawkbit.repository.builder.GenericSoftwareModuleMetadataUpdate;
|
||||
import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataBuilder;
|
||||
import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataCreate;
|
||||
import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataUpdate;
|
||||
import org.eclipse.hawkbit.repository.jpa.management.JpaSoftwareModuleManagement;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
|
||||
|
||||
/**
|
||||
* Builder implementation for {@link SoftwareModuleMetadata}.
|
||||
*/
|
||||
public class JpaSoftwareModuleMetadataBuilder implements SoftwareModuleMetadataBuilder {
|
||||
|
||||
private final JpaSoftwareModuleManagement softwareModuleManagement;
|
||||
|
||||
public JpaSoftwareModuleMetadataBuilder(final JpaSoftwareModuleManagement softwareModuleManagement) {
|
||||
this.softwareModuleManagement = softwareModuleManagement;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SoftwareModuleMetadataUpdate update(final long softwareModuleId, final String key) {
|
||||
return new GenericSoftwareModuleMetadataUpdate(softwareModuleId, key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SoftwareModuleMetadataCreate create(final long softwareModuleId) {
|
||||
return new JpaSoftwareModuleMetadataCreate(softwareModuleId, softwareModuleManagement);
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.builder;
|
||||
|
||||
import org.eclipse.hawkbit.repository.builder.AbstractSoftwareModuleMetadataUpdateCreate;
|
||||
import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataCreate;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.jpa.management.JpaSoftwareModuleManagement;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
|
||||
/**
|
||||
* Create/build implementation.
|
||||
*/
|
||||
public class JpaSoftwareModuleMetadataCreate
|
||||
extends AbstractSoftwareModuleMetadataUpdateCreate<SoftwareModuleMetadataCreate> implements SoftwareModuleMetadataCreate {
|
||||
|
||||
private final JpaSoftwareModuleManagement softwareModuleManagement;
|
||||
|
||||
JpaSoftwareModuleMetadataCreate(final long softwareModuleId, final JpaSoftwareModuleManagement softwareModuleManagement) {
|
||||
this.softwareModuleManagement = softwareModuleManagement;
|
||||
this.softwareModuleId = softwareModuleId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JpaSoftwareModuleMetadata build() {
|
||||
final JpaSoftwareModule module = softwareModuleManagement.get(softwareModuleId)
|
||||
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, softwareModuleId));
|
||||
|
||||
if (key == null) {
|
||||
new JpaSoftwareModuleMetadata(key, module, value, isTargetVisible().orElse(false));
|
||||
}
|
||||
|
||||
return new JpaSoftwareModuleMetadata(key, module, value, isTargetVisible().orElse(false));
|
||||
}
|
||||
}
|
||||
@@ -54,6 +54,7 @@ import org.eclipse.hawkbit.repository.MaintenanceScheduleHelper;
|
||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
||||
import org.eclipse.hawkbit.repository.RepositoryProperties;
|
||||
import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetTypeManagement;
|
||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||
import org.eclipse.hawkbit.repository.UpdateMode;
|
||||
@@ -82,7 +83,6 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
|
||||
import org.eclipse.hawkbit.repository.jpa.ql.EntityMatcher;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.ActionStatusRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleMetadataRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.specifications.ActionSpecifications;
|
||||
@@ -96,7 +96,6 @@ import org.eclipse.hawkbit.repository.model.AutoConfirmationStatus;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetType;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
@@ -143,7 +142,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
||||
private final DeploymentManagement deploymentManagement;
|
||||
private final ConfirmationManagement confirmationManagement;
|
||||
private final SoftwareModuleRepository softwareModuleRepository;
|
||||
private final SoftwareModuleMetadataRepository softwareModuleMetadataRepository;
|
||||
private final SoftwareModuleManagement<? extends SoftwareModule> softwareModuleManagement;
|
||||
private final DistributionSetManagement<? extends DistributionSet> distributionSetManagement;
|
||||
private final TenantConfigurationManagement tenantConfigurationManagement;
|
||||
private final ControllerPollProperties controllerPollProperties;
|
||||
@@ -162,7 +161,8 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
||||
final RepositoryProperties repositoryProperties,
|
||||
final TargetRepository targetRepository, final TargetTypeManagement targetTypeManagement,
|
||||
final DeploymentManagement deploymentManagement, final ConfirmationManagement confirmationManagement,
|
||||
final SoftwareModuleRepository softwareModuleRepository, final SoftwareModuleMetadataRepository softwareModuleMetadataRepository,
|
||||
final SoftwareModuleRepository softwareModuleRepository,
|
||||
final SoftwareModuleManagement<? extends SoftwareModule> softwareModuleManagement,
|
||||
final DistributionSetManagement<? extends DistributionSet> distributionSetManagement,
|
||||
final TenantConfigurationManagement tenantConfigurationManagement, final ControllerPollProperties controllerPollProperties,
|
||||
final PlatformTransactionManager txManager, final EntityFactory entityFactory, final EntityManager entityManager,
|
||||
@@ -176,7 +176,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
||||
this.deploymentManagement = deploymentManagement;
|
||||
this.confirmationManagement = confirmationManagement;
|
||||
this.softwareModuleRepository = softwareModuleRepository;
|
||||
this.softwareModuleMetadataRepository = softwareModuleMetadataRepository;
|
||||
this.softwareModuleManagement = softwareModuleManagement;
|
||||
this.distributionSetManagement = distributionSetManagement;
|
||||
this.tenantConfigurationManagement = tenantConfigurationManagement;
|
||||
this.controllerPollProperties = controllerPollProperties;
|
||||
@@ -281,11 +281,8 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Long, List<SoftwareModuleMetadata>> findTargetVisibleMetaDataBySoftwareModuleId(final Collection<Long> moduleId) {
|
||||
return softwareModuleMetadataRepository
|
||||
.findBySoftwareModuleIdInAndTargetVisible(moduleId, true, PageRequest.of(0, RepositoryConstants.MAX_META_DATA_COUNT))
|
||||
.getContent().stream()
|
||||
.collect(Collectors.groupingBy(o -> (Long) o[0], Collectors.mapping(o -> (SoftwareModuleMetadata) o[1], Collectors.toList())));
|
||||
public Map<Long, Map<String, String>> findTargetVisibleMetaDataBySoftwareModuleId(final Collection<Long> moduleId) {
|
||||
return systemSecurityContext.runAsSystem(() -> softwareModuleManagement.findMetaDataBySoftwareModuleIdsAndTargetVisible(moduleId));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -15,37 +15,30 @@ import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_M
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.UnaryOperator;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
|
||||
import org.eclipse.hawkbit.repository.ArtifactManagement;
|
||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
||||
import org.eclipse.hawkbit.repository.SoftwareModuleFields;
|
||||
import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
|
||||
import org.eclipse.hawkbit.repository.artifact.encryption.ArtifactEncryptionService;
|
||||
import org.eclipse.hawkbit.repository.builder.GenericSoftwareModuleMetadataUpdate;
|
||||
import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataCreate;
|
||||
import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataUpdate;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.LockedException;
|
||||
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
|
||||
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
|
||||
import org.eclipse.hawkbit.repository.jpa.builder.JpaSoftwareModuleMetadataCreate;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleMetadata;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleMetadata_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.SwMetadataCompositeKey;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule.JpaMetadataValue;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.helper.AfterTransactionCommitExecutorHolder;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleMetadataRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleTypeRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.specifications.SoftwareModuleSpecification;
|
||||
@@ -53,12 +46,12 @@ import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
|
||||
import org.eclipse.hawkbit.repository.model.Artifact;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule.MetadataValue;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||
import org.eclipse.hawkbit.utils.ObjectCopyUtil;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||
import org.springframework.dao.ConcurrencyFailureException;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
@@ -71,13 +64,13 @@ import org.springframework.util.ObjectUtils;
|
||||
@Service
|
||||
@ConditionalOnBooleanProperty(prefix = "hawkbit.jpa", name = { "enabled", "software-module-management" }, matchIfMissing = true)
|
||||
public class JpaSoftwareModuleManagement
|
||||
extends AbstractJpaRepositoryManagement<JpaSoftwareModule, SoftwareModuleManagement.Create, SoftwareModuleManagement.Update, SoftwareModuleRepository, SoftwareModuleFields>
|
||||
extends
|
||||
AbstractJpaRepositoryManagement<JpaSoftwareModule, SoftwareModuleManagement.Create, SoftwareModuleManagement.Update, SoftwareModuleRepository, SoftwareModuleFields>
|
||||
implements SoftwareModuleManagement<JpaSoftwareModule> {
|
||||
|
||||
protected static final String SOFTWARE_MODULE_METADATA = "SoftwareModuleMetadata";
|
||||
|
||||
private final DistributionSetRepository distributionSetRepository;
|
||||
private final SoftwareModuleMetadataRepository softwareModuleMetadataRepository;
|
||||
private final SoftwareModuleTypeRepository softwareModuleTypeRepository;
|
||||
private final ArtifactManagement artifactManagement;
|
||||
private final QuotaManagement quotaManagement;
|
||||
@@ -86,12 +79,10 @@ public class JpaSoftwareModuleManagement
|
||||
final SoftwareModuleRepository softwareModuleRepository,
|
||||
final EntityManager entityManager,
|
||||
final DistributionSetRepository distributionSetRepository,
|
||||
final SoftwareModuleMetadataRepository softwareModuleMetadataRepository,
|
||||
final SoftwareModuleTypeRepository softwareModuleTypeRepository,
|
||||
final ArtifactManagement artifactManagement, final QuotaManagement quotaManagement) {
|
||||
super(softwareModuleRepository, entityManager);
|
||||
this.distributionSetRepository = distributionSetRepository;
|
||||
this.softwareModuleMetadataRepository = softwareModuleMetadataRepository;
|
||||
this.softwareModuleTypeRepository = softwareModuleTypeRepository;
|
||||
this.artifactManagement = artifactManagement;
|
||||
this.quotaManagement = quotaManagement;
|
||||
@@ -176,86 +167,97 @@ public class JpaSoftwareModuleManagement
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
|
||||
public void createMetadata(final Collection<SoftwareModuleMetadataCreate> create) {
|
||||
// group by software module id to minimize database access
|
||||
create.stream()
|
||||
.map(JpaSoftwareModuleMetadataCreate.class::cast)
|
||||
.collect(Collectors.groupingBy(JpaSoftwareModuleMetadataCreate::getSoftwareModuleId))
|
||||
.forEach((id, createsForSoftwareModule) -> {
|
||||
assertSoftwareModuleExists(id);
|
||||
assertMetadataQuota(id, createsForSoftwareModule.size());
|
||||
|
||||
// touch to update revision and last modified timestamp
|
||||
JpaManagementHelper.touch(
|
||||
entityManager, jpaRepository, jpaRepository.findById(id).orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id)));
|
||||
createsForSoftwareModule.forEach(this::saveMetadata);
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@Override
|
||||
public List<SoftwareModuleMetadata> getMetadata(final long id) {
|
||||
assertSoftwareModuleExists(id);
|
||||
return (List) softwareModuleMetadataRepository.findAll(metadataBySoftwareModuleIdSpec(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public SoftwareModuleMetadata getMetadata(final long id, final String key) {
|
||||
assertSoftwareModuleExists(id);
|
||||
|
||||
return findMetadata(id, key).orElseThrow(() -> new EntityNotFoundException(SOFTWARE_MODULE_METADATA, id + ":" + key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<SoftwareModuleMetadata> findMetaDataBySoftwareModuleIdAndTargetVisible(final long id, final Pageable pageable) {
|
||||
assertSoftwareModuleExists(id);
|
||||
return JpaManagementHelper.convertPage(softwareModuleMetadataRepository.findBySoftwareModuleIdAndTargetVisible(
|
||||
id, true, PageRequest.of(0, RepositoryConstants.MAX_META_DATA_COUNT)), pageable);
|
||||
public void createMetadata(final Long id, final String key, final MetadataValue value) {
|
||||
final JpaSoftwareModule softwareModule = jpaRepository
|
||||
.findById(id)
|
||||
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id));
|
||||
final Map<String, JpaMetadataValue> metadataValueMap = softwareModule.getMetadata();
|
||||
final JpaMetadataValue existingValue = metadataValueMap.get(key);
|
||||
if (existingValue == null) {
|
||||
assertMetadataQuota(metadataValueMap.size() + 1L);
|
||||
}
|
||||
final JpaMetadataValue jpaMetadataValue = existingValue == null ?
|
||||
new JpaMetadataValue() : existingValue;
|
||||
if (ObjectCopyUtil.copy(value, jpaMetadataValue, true, UnaryOperator.identity())) {
|
||||
metadataValueMap.put(key, jpaMetadataValue);
|
||||
jpaRepository.save(softwareModule);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
|
||||
public SoftwareModuleMetadata updateMetadata(final SoftwareModuleMetadataCreate c) {
|
||||
final JpaSoftwareModuleMetadataCreate create = (JpaSoftwareModuleMetadataCreate) c;
|
||||
final Long id = create.getSoftwareModuleId();
|
||||
public void createMetadata(final Long id, final Map<String, ? extends MetadataValue> metadata) {
|
||||
final JpaSoftwareModule softwareModule = jpaRepository
|
||||
.findById(id)
|
||||
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id));
|
||||
final Map<String, JpaMetadataValue> metadataValueMap = softwareModule.getMetadata();
|
||||
assertMetadataQuota(metadata.keySet().stream().filter(key -> !metadataValueMap.containsKey(key)).count() + metadataValueMap.size());
|
||||
final AtomicBoolean changed = new AtomicBoolean(false);
|
||||
metadata.forEach((key, value) -> {
|
||||
final JpaMetadataValue jpaMetadataValue = metadataValueMap.getOrDefault(key, new JpaMetadataValue());
|
||||
if (ObjectCopyUtil.copy(value, jpaMetadataValue, true, UnaryOperator.identity())) {
|
||||
metadataValueMap.put(key, jpaMetadataValue);
|
||||
changed.set(true);
|
||||
}
|
||||
});
|
||||
if (changed.get()) {
|
||||
jpaRepository.save(softwareModule);
|
||||
}
|
||||
}
|
||||
|
||||
assertSoftwareModuleExists(id);
|
||||
assertMetadataQuota(id, 1);
|
||||
@Override
|
||||
public MetadataValue getMetadata(final Long id, final String key) {
|
||||
final JpaSoftwareModule softwareModule = jpaRepository
|
||||
.findById(id)
|
||||
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id));
|
||||
final MetadataValue metadataValue = softwareModule.getMetadata().get(key);
|
||||
if (metadataValue == null) {
|
||||
throw new EntityNotFoundException(SOFTWARE_MODULE_METADATA, id + ":" + key);
|
||||
} else {
|
||||
return metadataValue;
|
||||
}
|
||||
}
|
||||
|
||||
// touch to update revision and last modified timestamp
|
||||
JpaManagementHelper.touch(
|
||||
entityManager, jpaRepository, jpaRepository.findById(id).orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id)));
|
||||
return saveMetadata(create);
|
||||
@Override
|
||||
public Map<String, MetadataValue> getMetadata(final Long id) {
|
||||
return jpaRepository
|
||||
.findById(id)
|
||||
.map(JpaSoftwareModule::getMetadata)
|
||||
.map(metadata -> metadata.entrySet().stream()
|
||||
.collect(Collectors.toMap(
|
||||
Map.Entry::getKey,
|
||||
e -> (MetadataValue)e.getValue())))
|
||||
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
|
||||
public SoftwareModuleMetadata updateMetadata(final SoftwareModuleMetadataUpdate u) {
|
||||
final GenericSoftwareModuleMetadataUpdate update = (GenericSoftwareModuleMetadataUpdate) u;
|
||||
|
||||
// check if exists otherwise throw entity not found exception
|
||||
final JpaSoftwareModuleMetadata metadata = (JpaSoftwareModuleMetadata) findMetadata(
|
||||
update.getSoftwareModuleId(), update.getKey())
|
||||
.orElseThrow(() -> new EntityNotFoundException(SOFTWARE_MODULE_METADATA, update.getSoftwareModuleId() + ":" + update.getKey()));
|
||||
|
||||
update.getValue().ifPresent(metadata::setValue);
|
||||
update.isTargetVisible().ifPresent(metadata::setTargetVisible);
|
||||
|
||||
JpaManagementHelper.touch(entityManager, jpaRepository, metadata.getSoftwareModule());
|
||||
return softwareModuleMetadataRepository.save(metadata);
|
||||
public void deleteMetadata(final Long id, final String key) {
|
||||
final JpaSoftwareModule softwareModule = jpaRepository
|
||||
.findById(id)
|
||||
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id));
|
||||
final Map<String, JpaMetadataValue> metadataValueMap = softwareModule.getMetadata();
|
||||
if (!metadataValueMap.containsKey(key)) {
|
||||
throw new EntityNotFoundException(SOFTWARE_MODULE_METADATA, id + ":" + key);
|
||||
}
|
||||
metadataValueMap.remove(key);
|
||||
jpaRepository.save(softwareModule);
|
||||
}
|
||||
|
||||
// called only with 'system code' access, so no need to check access control
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
|
||||
public void deleteMetadata(final long id, final String key) {
|
||||
final JpaSoftwareModuleMetadata metadata = (JpaSoftwareModuleMetadata) findMetadata(id, key)
|
||||
.orElseThrow(() -> new EntityNotFoundException(SOFTWARE_MODULE_METADATA, id + ":" + key));
|
||||
|
||||
JpaManagementHelper.touch(entityManager, jpaRepository, metadata.getSoftwareModule());
|
||||
softwareModuleMetadataRepository.deleteById(metadata.getId());
|
||||
public Map<Long, Map<String, String>> findMetaDataBySoftwareModuleIdsAndTargetVisible(final Collection<Long> ids) {
|
||||
return jpaRepository.findVisibleMetadataByModuleIds(ids)
|
||||
.stream()
|
||||
.collect(Collectors.groupingBy(
|
||||
entry -> (Long) entry[0],
|
||||
Collectors.toMap(
|
||||
entry -> (String) entry[1],
|
||||
entry -> (String) entry[2],
|
||||
(existing, replacement) -> existing, // in case of duplicates, keep the first one
|
||||
HashMap::new)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -340,14 +342,6 @@ public class JpaSoftwareModuleManagement
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Long, List<SoftwareModuleMetadata>> findMetaDataBySoftwareModuleIdsAndTargetVisible(final Collection<Long> moduleIds) {
|
||||
return softwareModuleMetadataRepository
|
||||
.findBySoftwareModuleIdInAndTargetVisible(moduleIds, true, PageRequest.of(0, RepositoryConstants.MAX_META_DATA_COUNT))
|
||||
.getContent().stream()
|
||||
.collect(Collectors.groupingBy(o -> (Long) o[0], Collectors.mapping(o -> (SoftwareModuleMetadata) o[1], Collectors.toList())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countByAssignedTo(final long distributionSetId) {
|
||||
assertDistributionSetExists(distributionSetId);
|
||||
@@ -355,10 +349,6 @@ public class JpaSoftwareModuleManagement
|
||||
return jpaRepository.count(SoftwareModuleSpecification.byAssignedToDs(distributionSetId));
|
||||
}
|
||||
|
||||
private static Specification<JpaSoftwareModuleMetadata> metadataBySoftwareModuleIdSpec(final long id) {
|
||||
return (root, query, cb) -> cb.equal(root.get(JpaSoftwareModuleMetadata_.softwareModule).get(AbstractJpaBaseEntity_.id), id);
|
||||
}
|
||||
|
||||
private void deleteGridFsArtifacts(final JpaSoftwareModule swModule) {
|
||||
jpaRepository.getAccessController().ifPresent(accessController ->
|
||||
accessController.assertOperationAllowed(AccessController.Operation.DELETE, swModule));
|
||||
@@ -374,41 +364,14 @@ public class JpaSoftwareModuleManagement
|
||||
smFilterNameAndVersionEntries[1]);
|
||||
}
|
||||
|
||||
private SoftwareModuleMetadata saveMetadata(final JpaSoftwareModuleMetadataCreate create) {
|
||||
assertSoftwareModuleMetadataDoesNotExist(create.getSoftwareModuleId(), create);
|
||||
return softwareModuleMetadataRepository.save(create.build());
|
||||
}
|
||||
|
||||
private Optional<SoftwareModuleMetadata> findMetadata(final long id, final String key) {
|
||||
assertSoftwareModuleExists(id);
|
||||
|
||||
return softwareModuleMetadataRepository.findById(new SwMetadataCompositeKey(id, key))
|
||||
.map(SoftwareModuleMetadata.class::cast);
|
||||
}
|
||||
|
||||
private void assertSoftwareModuleMetadataDoesNotExist(final Long id, final JpaSoftwareModuleMetadataCreate md) {
|
||||
if (softwareModuleMetadataRepository.existsById(new SwMetadataCompositeKey(id, md.getKey()))) {
|
||||
throw new EntityAlreadyExistsException("Metadata entry with key '" + md.getKey() + "' already exists!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts the meta-data quota for the software module with the given ID.
|
||||
*
|
||||
* @param id The software module ID.
|
||||
* @param requested Number of meta-data entries to be created.
|
||||
*/
|
||||
private void assertMetadataQuota(final Long id, final int requested) {
|
||||
private void assertMetadataQuota(final long requested) {
|
||||
final int maxMetaData = quotaManagement.getMaxMetaDataEntriesPerSoftwareModule();
|
||||
QuotaHelper.assertAssignmentQuota(
|
||||
id, requested, maxMetaData, SoftwareModuleMetadata.class, SoftwareModule.class,
|
||||
softwareModuleMetadataRepository::countBySoftwareModuleId);
|
||||
}
|
||||
|
||||
private void assertSoftwareModuleExists(final Long id) {
|
||||
if (!jpaRepository.existsById(id)) {
|
||||
throw new EntityNotFoundException(SoftwareModule.class, id);
|
||||
}
|
||||
QuotaHelper.assertAssignmentQuota(requested, maxMetaData, SoftwareModule.MetadataValueCreate.class, SoftwareModule.class);
|
||||
}
|
||||
|
||||
private void assertSoftwareModuleTypeExists(final Long typeId) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
* Copyright (c) 2025 Contributors to the Eclipse Foundation
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
@@ -10,6 +10,7 @@
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -20,6 +21,7 @@ import jakarta.persistence.CollectionTable;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.ConstraintMode;
|
||||
import jakarta.persistence.ElementCollection;
|
||||
import jakarta.persistence.Embeddable;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.ForeignKey;
|
||||
@@ -36,7 +38,7 @@ import jakarta.persistence.UniqueConstraint;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
@@ -102,8 +104,7 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement
|
||||
joinColumns = { @JoinColumn(name = "sm", nullable = false) },
|
||||
foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_sm_metadata_sm"))
|
||||
@MapKeyColumn(name = "meta_key", length = SoftwareModule.METADATA_KEY_MAX_SIZE)
|
||||
@Column(name = "meta_value", length = SoftwareModule.METADATA_VALUE_MAX_SIZE)
|
||||
private Map<String, String> metadata;
|
||||
private Map<String, JpaMetadataValue> metadata;
|
||||
|
||||
@Column(name = "locked")
|
||||
private boolean locked;
|
||||
@@ -192,4 +193,18 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement
|
||||
public void fireDeleteEvent() {
|
||||
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new SoftwareModuleDeletedEvent(getTenant(), getId(), getClass()));
|
||||
}
|
||||
|
||||
@Data
|
||||
@Embeddable
|
||||
public static class JpaMetadataValue implements MetadataValue, Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Column(name = "meta_value", length = SoftwareModule.METADATA_VALUE_MAX_SIZE)
|
||||
@Size(max = METADATA_VALUE_MAX_SIZE)
|
||||
private String value;
|
||||
@Column(name = "target_visible")
|
||||
private boolean targetVisible;
|
||||
}
|
||||
}
|
||||
@@ -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.model;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
import jakarta.persistence.Basic;
|
||||
import jakarta.persistence.Column;
|
||||
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 jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
|
||||
|
||||
/**
|
||||
* Metadata for {@link SoftwareModule}.
|
||||
*/
|
||||
@NoArgsConstructor // Default constructor for JPA
|
||||
@Data
|
||||
@IdClass(SwMetadataCompositeKey.class)
|
||||
@Entity
|
||||
@Table(name = "sp_sm_metadata")
|
||||
public class JpaSoftwareModuleMetadata implements SoftwareModuleMetadata {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
||||
@JoinColumn(
|
||||
name = "sm", nullable = false, updatable = false,
|
||||
foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_sm_metadata_sm"))
|
||||
private JpaSoftwareModule softwareModule;
|
||||
|
||||
@Id
|
||||
@Column(name = "meta_key", nullable = false, length = SoftwareModule.METADATA_KEY_MAX_SIZE, updatable = false)
|
||||
@Size(min = 1, max = SoftwareModule.METADATA_KEY_MAX_SIZE)
|
||||
@NotNull
|
||||
private String key;
|
||||
|
||||
@Column(name = "meta_value", length = SoftwareModule.METADATA_VALUE_MAX_SIZE)
|
||||
@Size(max = SoftwareModule.METADATA_VALUE_MAX_SIZE)
|
||||
@Basic
|
||||
private String value;
|
||||
|
||||
@Column(name = "target_visible")
|
||||
private boolean targetVisible;
|
||||
|
||||
public JpaSoftwareModuleMetadata(final String key, final SoftwareModule softwareModule, final String value) {
|
||||
this(key, softwareModule, value, false);
|
||||
}
|
||||
|
||||
public JpaSoftwareModuleMetadata(final String key, final SoftwareModule softwareModule, final String value, final boolean targetVisible) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
this.softwareModule = (JpaSoftwareModule) softwareModule;
|
||||
this.targetVisible = targetVisible;
|
||||
}
|
||||
|
||||
public SwMetadataCompositeKey getId() {
|
||||
return new SwMetadataCompositeKey(softwareModule.getId(), getKey());
|
||||
}
|
||||
}
|
||||
@@ -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.model;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* The Software Module meta-data composite key which contains the meta-data key and the ID of the software module itself.
|
||||
*/
|
||||
@NoArgsConstructor // Default constructor for JPA
|
||||
@Data
|
||||
public final class SwMetadataCompositeKey implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String key;
|
||||
private Long softwareModule;
|
||||
|
||||
/**
|
||||
* @param moduleId the software module for this meta-data
|
||||
* @param key the key of the meta-data
|
||||
*/
|
||||
public SwMetadataCompositeKey(final Long moduleId, final String key) {
|
||||
this.softwareModule = moduleId;
|
||||
this.key = key;
|
||||
}
|
||||
}
|
||||
@@ -1,69 +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 java.util.Collection;
|
||||
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleMetadata;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.SwMetadataCompositeKey;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
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 SoftwareModuleMetadata} repository.
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public interface SoftwareModuleMetadataRepository
|
||||
extends PagingAndSortingRepository<JpaSoftwareModuleMetadata, SwMetadataCompositeKey>,
|
||||
CrudRepository<JpaSoftwareModuleMetadata, SwMetadataCompositeKey>,
|
||||
JpaSpecificationExecutor<JpaSoftwareModuleMetadata> {
|
||||
|
||||
/**
|
||||
* Locates the meta-data entries that match the given software module ID and target visibility flag.
|
||||
*
|
||||
* @param moduleId The ID of the software module.
|
||||
* @param targetVisible The target visibility flag.
|
||||
* @param page The pagination parameters.
|
||||
* @return A {@link Page} with the matching meta-data entries.
|
||||
*/
|
||||
Page<JpaSoftwareModuleMetadata> findBySoftwareModuleIdAndTargetVisible(
|
||||
@Param("moduleId") Long moduleId, @Param("targetVisible") boolean targetVisible, Pageable page);
|
||||
|
||||
/**
|
||||
* Locates the meta-data entries that match the given software module IDs and target visibility flag.
|
||||
* <p/>
|
||||
* No access control applied
|
||||
*
|
||||
* @param moduleId List of software module IDs.
|
||||
* @param targetVisible The target visibility flag.
|
||||
* @param page The pagination parameters.
|
||||
* @return A {@link Page} with the matching meta-data entries.
|
||||
*/
|
||||
@Query("SELECT smd.softwareModule.id, smd FROM JpaSoftwareModuleMetadata smd WHERE smd.softwareModule.id IN :moduleId AND smd.targetVisible = :targetVisible")
|
||||
Page<Object[]> findBySoftwareModuleIdInAndTargetVisible(
|
||||
@Param("moduleId") Collection<Long> moduleId, @Param("targetVisible") boolean targetVisible, Pageable page);
|
||||
|
||||
/**
|
||||
* Counts the meta-data entries that are associated with the addressed software module.
|
||||
* <p/>
|
||||
* No access control applied
|
||||
*
|
||||
* @param moduleId The ID of the software module.
|
||||
* @return The number of meta-data entries associated with the software module.
|
||||
*/
|
||||
long countBySoftwareModuleId(@Param("moduleId") Long moduleId);
|
||||
}
|
||||
@@ -9,6 +9,9 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.repository;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
|
||||
@@ -47,6 +50,9 @@ public interface SoftwareModuleRepository extends BaseEntityRepository<JpaSoftwa
|
||||
*/
|
||||
long countByAssignedToId(Long distributionSetId);
|
||||
|
||||
@Query("SELECT sm.id, KEY(m), m.value FROM JpaSoftwareModule sm JOIN sm.metadata m WHERE sm.id IN :ids AND m.targetVisible = true")
|
||||
List<Object[]> findVisibleMetadataByModuleIds(@Param("ids") Collection<Long> ids);
|
||||
|
||||
/**
|
||||
* Deletes all {@link TenantAwareBaseEntity} of a given tenant. For safety
|
||||
* reasons (this is a "delete everything" query after all) we add the tenant
|
||||
|
||||
@@ -44,7 +44,6 @@ import org.eclipse.hawkbit.repository.jpa.repository.LocalArtifactRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.RolloutGroupRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.RolloutRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.RolloutTargetGroupRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleMetadataRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleTypeRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
|
||||
@@ -111,8 +110,6 @@ public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest
|
||||
@Autowired
|
||||
protected DistributionSetTagRepository distributionSetTagRepository;
|
||||
@Autowired
|
||||
protected SoftwareModuleMetadataRepository softwareModuleMetadataRepository;
|
||||
@Autowired
|
||||
protected ActionStatusRepository actionStatusRepository;
|
||||
@Autowired
|
||||
protected LocalArtifactRepository artifactRepository;
|
||||
|
||||
@@ -73,7 +73,6 @@ import org.eclipse.hawkbit.repository.model.ArtifactUpload;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
import org.eclipse.hawkbit.repository.test.matcher.Expect;
|
||||
@@ -989,7 +988,7 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
final DistributionSet set = testdataFactory.createDistributionSet();
|
||||
testdataFactory.addSoftwareModuleMetadata(set);
|
||||
|
||||
final Map<Long, List<SoftwareModuleMetadata>> result = controllerManagement
|
||||
final Map<Long, Map<String, String>> result = controllerManagement
|
||||
.findTargetVisibleMetaDataBySoftwareModuleId(set.getModules().stream().map(SoftwareModule::getId).toList());
|
||||
|
||||
assertThat(result).hasSize(3);
|
||||
|
||||
@@ -10,12 +10,15 @@
|
||||
package org.eclipse.hawkbit.repository.jpa.management;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||
import org.eclipse.hawkbit.im.authentication.SpRole;
|
||||
import org.eclipse.hawkbit.repository.RepositoryManagement;
|
||||
import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
|
||||
import org.eclipse.hawkbit.repository.jpa.AbstractRepositoryManagementSecurityTest;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule.MetadataValueCreate;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
@@ -46,11 +49,15 @@ class SoftwareManagementSecurityTest
|
||||
@Test
|
||||
void createMetaDataPermissionsCheck() {
|
||||
assertPermissions(
|
||||
() -> softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().create(1L).key("key").value("value")),
|
||||
() -> {
|
||||
softwareModuleManagement.createMetadata(1L, "key", new MetadataValueCreate("value"));
|
||||
return null;
|
||||
},
|
||||
List.of(SpPermission.UPDATE_REPOSITORY));
|
||||
assertPermissions(() -> {
|
||||
softwareModuleManagement.createMetadata(
|
||||
List.of(entityFactory.softwareModuleMetadata().create(1L).key("key").value("value")));
|
||||
1L,
|
||||
Map.of("key", new MetadataValueCreate("value")));
|
||||
return null;
|
||||
}, List.of(SpPermission.UPDATE_REPOSITORY));
|
||||
}
|
||||
@@ -112,15 +119,6 @@ class SoftwareManagementSecurityTest
|
||||
assertPermissions(() -> softwareModuleManagement.getMetadata(1L), List.of(SpPermission.READ_REPOSITORY));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
|
||||
*/
|
||||
@Test
|
||||
void findMetaDataBySoftwareModuleIdAndTargetVisiblePermissionsCheck() {
|
||||
assertPermissions(() -> softwareModuleManagement.findMetaDataBySoftwareModuleIdAndTargetVisible(1L, PAGE),
|
||||
List.of(SpPermission.READ_REPOSITORY));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
|
||||
*/
|
||||
@@ -157,7 +155,10 @@ class SoftwareManagementSecurityTest
|
||||
@Test
|
||||
void updateMetaDataPermissionsCheck() {
|
||||
assertPermissions(
|
||||
() -> softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().update(1L, "key").value("value")),
|
||||
() -> {
|
||||
softwareModuleManagement.createMetadata(1L, "key", new MetadataValueCreate("value"));
|
||||
return null;
|
||||
},
|
||||
List.of(SpPermission.UPDATE_REPOSITORY));
|
||||
}
|
||||
|
||||
@@ -167,6 +168,6 @@ class SoftwareManagementSecurityTest
|
||||
@Test
|
||||
void findMetaDataBySoftwareModuleIdsAndTargetVisiblePermissionsCheck() {
|
||||
assertPermissions(() -> softwareModuleManagement.findMetaDataBySoftwareModuleIdsAndTargetVisible(List.of(1L)),
|
||||
List.of(SpPermission.READ_REPOSITORY));
|
||||
List.of(SpRole.SYSTEM_ROLE));
|
||||
}
|
||||
}
|
||||
@@ -14,11 +14,11 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -26,7 +26,6 @@ import jakarta.validation.ConstraintViolationException;
|
||||
|
||||
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
||||
import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
|
||||
import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataCreate;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||
@@ -37,7 +36,6 @@ import org.eclipse.hawkbit.repository.jpa.RandomGeneratedInputStream;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleMetadata;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
@@ -45,7 +43,8 @@ import org.eclipse.hawkbit.repository.model.Artifact;
|
||||
import org.eclipse.hawkbit.repository.model.ArtifactUpload;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule.MetadataValue;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule.MetadataValueCreate;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
@@ -53,8 +52,6 @@ import org.eclipse.hawkbit.repository.test.matcher.Expect;
|
||||
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.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
|
||||
/**
|
||||
* Feature: Component Tests - Repository<br/>
|
||||
@@ -62,8 +59,6 @@ import org.springframework.data.domain.PageRequest;
|
||||
*/
|
||||
class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
private static final PageRequest PAGE_REQUEST_100 = PageRequest.of(0, 100);
|
||||
|
||||
/**
|
||||
* Verifies that management get access reacts as specified on calls for non existing entities by means
|
||||
* of Optional not present.
|
||||
@@ -72,14 +67,10 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
|
||||
@ExpectEvents({ @Expect(type = SoftwareModuleCreatedEvent.class, count = 1) })
|
||||
void nonExistingEntityAccessReturnsNotPresent() {
|
||||
final SoftwareModule module = testdataFactory.createSoftwareModuleApp();
|
||||
|
||||
assertThat(softwareModuleManagement.get(1234L)).isNotPresent();
|
||||
|
||||
assertThat(softwareModuleManagement.findByNameAndVersionAndType(NOT_EXIST_ID, NOT_EXIST_ID, osType.getId()))
|
||||
.isNotPresent();
|
||||
|
||||
assertThatExceptionOfType(EntityNotFoundException.class)
|
||||
.isThrownBy(() -> softwareModuleManagement.getMetadata(module.getId(), NOT_EXIST_ID));
|
||||
assertThat(softwareModuleManagement.findByNameAndVersionAndType(NOT_EXIST_ID, NOT_EXIST_ID, osType.getId())).isNotPresent();
|
||||
final Long moduleId = module.getId();
|
||||
assertThatExceptionOfType(EntityNotFoundException.class).isThrownBy(() -> softwareModuleManagement.getMetadata(moduleId, NOT_EXIST_ID));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,7 +80,7 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
|
||||
@Test
|
||||
@ExpectEvents({ @Expect(type = SoftwareModuleCreatedEvent.class, count = 1) })
|
||||
void entityQueriesReferringToNotExistingEntitiesThrowsException() {
|
||||
final SoftwareModule module = testdataFactory.createSoftwareModuleApp();
|
||||
testdataFactory.createSoftwareModuleApp();
|
||||
|
||||
final SoftwareModuleManagement.Create noType = SoftwareModuleManagement.Create.builder().name("xxx").type(null).build();
|
||||
final List<SoftwareModuleManagement.Create> noTypeList = List.of(noType);
|
||||
@@ -98,22 +89,20 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
|
||||
assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(() -> softwareModuleManagement.create(noType));
|
||||
|
||||
verifyThrownExceptionBy(
|
||||
() -> softwareModuleManagement.updateMetadata(
|
||||
entityFactory.softwareModuleMetadata().create(NOT_EXIST_IDL).key("xxx").value("xxx")), "SoftwareModule");
|
||||
() -> softwareModuleManagement.createMetadata(
|
||||
NOT_EXIST_IDL, "xxx", new MetadataValueCreate("xxx")), "SoftwareModule");
|
||||
verifyThrownExceptionBy(
|
||||
() -> softwareModuleManagement.createMetadata(Collections.singletonList(
|
||||
entityFactory.softwareModuleMetadata().create(NOT_EXIST_IDL).key("xxx").value("xxx"))), "SoftwareModule");
|
||||
() -> softwareModuleManagement.createMetadata(
|
||||
NOT_EXIST_IDL,
|
||||
Map.of("xxx", new MetadataValueCreate("xxx"))), "SoftwareModule");
|
||||
|
||||
verifyThrownExceptionBy(() -> softwareModuleManagement.delete(NOT_EXIST_IDL), "SoftwareModule");
|
||||
verifyThrownExceptionBy(() -> softwareModuleManagement.delete(Collections.singletonList(NOT_EXIST_IDL)), "SoftwareModule");
|
||||
verifyThrownExceptionBy(() -> softwareModuleManagement.deleteMetadata(NOT_EXIST_IDL, "xxx"), "SoftwareModule");
|
||||
|
||||
verifyThrownExceptionBy(
|
||||
() -> softwareModuleManagement.updateMetadata(
|
||||
entityFactory.softwareModuleMetadata().update(NOT_EXIST_IDL, "xxx").value("xxx")), "SoftwareModule");
|
||||
verifyThrownExceptionBy(
|
||||
() -> softwareModuleManagement.updateMetadata(
|
||||
entityFactory.softwareModuleMetadata().update(module.getId(), NOT_EXIST_ID).value("xxx")), "SoftwareModuleMetadata");
|
||||
() -> softwareModuleManagement.createMetadata(
|
||||
NOT_EXIST_IDL, "xxx", new MetadataValueCreate("xxx")), "SoftwareModule");
|
||||
|
||||
verifyThrownExceptionBy(() -> softwareModuleManagement.findByAssignedTo(NOT_EXIST_IDL, PAGE), "DistributionSet");
|
||||
|
||||
@@ -241,8 +230,8 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
|
||||
testdataFactory.createSoftwareModuleApp();
|
||||
|
||||
assertThat((List) softwareModuleManagement.findByType(osType.getId(), PAGE).getContent())
|
||||
.as("Expected to find the following number of modules:").hasSize(2).as("with the following elements")
|
||||
.contains(two, one);
|
||||
.as("Expected to find the following number of modules:").hasSize(2)
|
||||
.as("with the following elements").contains(two, one);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -525,30 +514,21 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
assertThat(softwareModule.getOptLockRevision()).isEqualTo(1);
|
||||
|
||||
final SoftwareModuleMetadataCreate swMetadata1 = entityFactory.softwareModuleMetadata()
|
||||
.create(softwareModule.getId())
|
||||
.key(knownKey1)
|
||||
.value(knownValue1)
|
||||
.targetVisible(true);
|
||||
|
||||
final SoftwareModuleMetadataCreate swMetadata2 = entityFactory.softwareModuleMetadata()
|
||||
.create(softwareModule.getId())
|
||||
.key(knownKey2)
|
||||
.value(knownValue2);
|
||||
|
||||
softwareModuleManagement.createMetadata(Arrays.asList(swMetadata1, swMetadata2));
|
||||
softwareModuleManagement.createMetadata(
|
||||
softwareModule.getId(),
|
||||
Map.of(
|
||||
knownKey1, new MetadataValueCreate(knownValue1, true),
|
||||
knownKey2, new MetadataValueCreate(knownValue2)));
|
||||
|
||||
final SoftwareModule changedLockRevisionModule = softwareModuleManagement.get(softwareModule.getId()).get();
|
||||
assertThat(changedLockRevisionModule.getOptLockRevision()).isEqualTo(2);
|
||||
|
||||
assertThat(softwareModuleManagement.getMetadata(softwareModule.getId(), knownKey1)).satisfies(metadata -> {
|
||||
assertThat(metadata.getKey()).isEqualTo(knownKey1);
|
||||
assertThat(metadata.getValue()).isEqualTo(knownValue1);
|
||||
assertThat(metadata.isTargetVisible()).isTrue();
|
||||
});
|
||||
|
||||
assertThat(softwareModuleManagement.getMetadata(softwareModule.getId(), knownKey2)).satisfies(metadata -> {
|
||||
assertThat(metadata.getKey()).isEqualTo(knownKey2);
|
||||
assertThat(metadata.getValue()).isEqualTo(knownValue2);
|
||||
assertThat(metadata.isTargetVisible()).isFalse();
|
||||
});
|
||||
@@ -559,80 +539,49 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
|
||||
*/
|
||||
@Test
|
||||
void createSoftwareModuleMetadataUntilQuotaIsExceeded() {
|
||||
|
||||
// add meta data one by one
|
||||
// add meta-data one by one
|
||||
final SoftwareModule module = testdataFactory.createSoftwareModuleApp("m1");
|
||||
final int maxMetaData = quotaManagement.getMaxMetaDataEntriesPerSoftwareModule();
|
||||
for (int i = 0; i < maxMetaData; ++i) {
|
||||
softwareModuleManagement.updateMetadata(
|
||||
entityFactory.softwareModuleMetadata().create(module.getId()).key("k" + i).value("v" + i));
|
||||
softwareModuleManagement.createMetadata(module.getId(), "k" + i, new MetadataValueCreate("v" + i));
|
||||
}
|
||||
|
||||
// quota exceeded
|
||||
final SoftwareModuleMetadataCreate metadata = entityFactory.softwareModuleMetadata().create(module.getId())
|
||||
.key("k" + maxMetaData).value("v" + maxMetaData);
|
||||
final Long moduleId = module.getId();
|
||||
final MetadataValueCreate metadata = new MetadataValueCreate("v" + maxMetaData);
|
||||
assertThatExceptionOfType(AssignmentQuotaExceededException.class)
|
||||
.isThrownBy(() -> softwareModuleManagement.updateMetadata(metadata));
|
||||
.isThrownBy(() -> softwareModuleManagement.createMetadata(moduleId, "k" + maxMetaData, metadata));
|
||||
|
||||
// add multiple meta data entries at once
|
||||
final SoftwareModule module2 = testdataFactory.createSoftwareModuleApp("m2");
|
||||
final List<SoftwareModuleMetadataCreate> create = new ArrayList<>();
|
||||
final Long moduleId2 = module2.getId();
|
||||
final Map<String, MetadataValueCreate> create = new HashMap<>();
|
||||
for (int i = 0; i < maxMetaData + 1; ++i) {
|
||||
create.add(entityFactory.softwareModuleMetadata().create(module2.getId()).key("k" + i).value("v" + i));
|
||||
create.put("k" + i, new MetadataValueCreate("v" + i));
|
||||
}
|
||||
// quota exceeded
|
||||
assertThatExceptionOfType(AssignmentQuotaExceededException.class)
|
||||
.isThrownBy(() -> softwareModuleManagement.createMetadata(create));
|
||||
.isThrownBy(() -> softwareModuleManagement.createMetadata(moduleId2, create));
|
||||
|
||||
// add some meta data entries
|
||||
final SoftwareModule module3 = testdataFactory.createSoftwareModuleApp("m3");
|
||||
final int firstHalf = Math.round((maxMetaData) / 2.f);
|
||||
for (int i = 0; i < firstHalf; ++i) {
|
||||
softwareModuleManagement.updateMetadata(
|
||||
entityFactory.softwareModuleMetadata().create(module3.getId()).key("k" + i).value("v" + i));
|
||||
softwareModuleManagement.createMetadata(module3.getId(), "k" + i, new MetadataValueCreate("v" + i));
|
||||
}
|
||||
// add too many data entries
|
||||
final Long moduleId3 = module3.getId();
|
||||
final int secondHalf = maxMetaData - firstHalf;
|
||||
final List<SoftwareModuleMetadataCreate> create2 = new ArrayList<>();
|
||||
final Map<String, MetadataValueCreate> create2 = new HashMap<>();
|
||||
for (int i = 0; i < secondHalf + 1; ++i) {
|
||||
create2.add(entityFactory.softwareModuleMetadata().create(module3.getId()).key("kk" + i).value("vv" + i));
|
||||
create2.put("kk" + i, new MetadataValueCreate("vv" + i));
|
||||
}
|
||||
// quota exceeded
|
||||
assertThatExceptionOfType(AssignmentQuotaExceededException.class)
|
||||
.isThrownBy(() -> softwareModuleManagement.createMetadata(create2));
|
||||
.isThrownBy(() -> softwareModuleManagement.createMetadata(moduleId3, create2));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that metadata for a software module cannot be created for an existing key.
|
||||
*/
|
||||
@Test
|
||||
void createSoftwareModuleMetadataFailsIfKeyExists() {
|
||||
|
||||
final String knownKey1 = "myKnownKey1";
|
||||
final String knownValue1 = "myKnownValue1";
|
||||
final SoftwareModule ah = testdataFactory.createSoftwareModuleApp();
|
||||
|
||||
final SoftwareModuleMetadataCreate metadata = entityFactory.softwareModuleMetadata()
|
||||
.create(ah.getId()).key(knownKey1).value(knownValue1).targetVisible(true);
|
||||
softwareModuleManagement.updateMetadata(metadata);
|
||||
|
||||
assertThatExceptionOfType(EntityAlreadyExistsException.class)
|
||||
.isThrownBy(() -> softwareModuleManagement.updateMetadata(metadata))
|
||||
.withMessageContaining("Metadata").withMessageContaining(knownKey1);
|
||||
|
||||
final String knownKey2 = "myKnownKey2";
|
||||
|
||||
softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().create(ah.getId()).key(knownKey2)
|
||||
.value(knownValue1).targetVisible(false));
|
||||
|
||||
final SoftwareModuleMetadataCreate metadata2 = entityFactory.softwareModuleMetadata().create(ah.getId())
|
||||
.key(knownKey2).value(knownValue1).targetVisible(true);
|
||||
assertThatExceptionOfType(EntityAlreadyExistsException.class)
|
||||
.isThrownBy(() -> softwareModuleManagement.updateMetadata(metadata2))
|
||||
.withMessageContaining("Metadata").withMessageContaining(knownKey2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that metadata for a software module can be updated.
|
||||
*/
|
||||
@@ -649,8 +598,8 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
|
||||
assertThat(ah.getOptLockRevision()).isEqualTo(1);
|
||||
|
||||
// create an software module meta data entry
|
||||
final SoftwareModuleMetadata softwareModuleMetadata = softwareModuleManagement.updateMetadata(
|
||||
entityFactory.softwareModuleMetadata().create(ah.getId()).key(knownKey).value(knownValue));
|
||||
softwareModuleManagement.createMetadata(ah.getId(), knownKey, new MetadataValueCreate(knownValue));
|
||||
final MetadataValue softwareModuleMetadata = softwareModuleManagement.getMetadata(ah.getId(), knownKey);
|
||||
assertThat(softwareModuleMetadata.isTargetVisible()).isFalse();
|
||||
assertThat(softwareModuleMetadata.getValue()).isEqualTo(knownValue);
|
||||
|
||||
@@ -660,8 +609,8 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
|
||||
assertThat(changedLockRevisionModule.getOptLockRevision()).isEqualTo(2);
|
||||
|
||||
// update the software module metadata
|
||||
final SoftwareModuleMetadata updated = softwareModuleManagement.updateMetadata(entityFactory
|
||||
.softwareModuleMetadata().update(ah.getId(), knownKey).value(knownUpdateValue).targetVisible(true));
|
||||
softwareModuleManagement.createMetadata(ah.getId(), knownKey, new MetadataValueCreate(knownUpdateValue, true));
|
||||
final MetadataValue updated = softwareModuleManagement.getMetadata(ah.getId(), knownKey);
|
||||
|
||||
// we are updating the sw metadata so also modifying the base software
|
||||
// module so opt lock revision must be two
|
||||
@@ -672,8 +621,6 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
|
||||
assertThat(updated).isNotNull();
|
||||
assertThat(updated.getValue()).isEqualTo(knownUpdateValue);
|
||||
assertThat(updated.isTargetVisible()).isTrue();
|
||||
assertThat(((JpaSoftwareModuleMetadata) updated).getId().getKey()).isEqualTo(knownKey);
|
||||
assertThat(((JpaSoftwareModuleMetadata) updated).getSoftwareModule().getId()).isEqualTo(ah.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -686,37 +633,18 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
final SoftwareModule swModule = testdataFactory.createSoftwareModuleApp();
|
||||
|
||||
softwareModuleManagement.updateMetadata(
|
||||
entityFactory.softwareModuleMetadata().create(swModule.getId()).key(knownKey1).value(knownValue1));
|
||||
softwareModuleManagement.createMetadata(swModule.getId(), knownKey1, new MetadataValueCreate(knownValue1));
|
||||
|
||||
assertThat(softwareModuleManagement.getMetadata(swModule.getId()))
|
||||
assertThat(softwareModuleManagement.getMetadata(swModule.getId()).entrySet())
|
||||
.as("Contains the created metadata element").allSatisfy(metadata -> {
|
||||
assertThat(((JpaSoftwareModuleMetadata) metadata).getSoftwareModule().getId()).isEqualTo(swModule.getId());
|
||||
assertThat(metadata.getKey()).isEqualTo(knownKey1);
|
||||
assertThat(metadata.getValue()).isEqualTo(knownValue1);
|
||||
assertThat(metadata.getValue().getValue()).isEqualTo(knownValue1);
|
||||
});
|
||||
|
||||
softwareModuleManagement.deleteMetadata(swModule.getId(), knownKey1);
|
||||
assertThat(softwareModuleManagement.getMetadata(swModule.getId())).as("Metadata elements are").isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that non existing metadata find results in exception.
|
||||
*/
|
||||
@Test
|
||||
void findSoftwareModuleMetadataFailsIfEntryDoesNotExist() {
|
||||
final String knownKey1 = "myKnownKey1";
|
||||
final String knownValue1 = "myKnownValue1";
|
||||
|
||||
final SoftwareModule ah = testdataFactory.createSoftwareModuleApp();
|
||||
|
||||
softwareModuleManagement.updateMetadata(
|
||||
entityFactory.softwareModuleMetadata().create(ah.getId()).key(knownKey1).value(knownValue1));
|
||||
|
||||
assertThatExceptionOfType(EntityNotFoundException.class)
|
||||
.isThrownBy(() -> softwareModuleManagement.getMetadata(ah.getId(), "doesnotexist"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries and loads the metadata related to a given software module.
|
||||
*/
|
||||
@@ -729,28 +657,24 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
|
||||
final int metadataCountSw2 = 10;
|
||||
|
||||
for (int index = 0; index < metadataCountSw1; index++) {
|
||||
softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().create(sw1.getId())
|
||||
.key("key" + index).value("value" + index).targetVisible(true));
|
||||
softwareModuleManagement.createMetadata(sw1.getId(), "key" + index, new MetadataValueCreate("value" + index, true));
|
||||
}
|
||||
|
||||
for (int index = 0; index < metadataCountSw2; index++) {
|
||||
softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().create(sw2.getId())
|
||||
.key("key" + index).value("value" + index).targetVisible(false));
|
||||
softwareModuleManagement.createMetadata(sw2.getId(), "key" + index, new MetadataValueCreate("value" + index, false));
|
||||
}
|
||||
|
||||
final List<SoftwareModuleMetadata> metadataSw1 = softwareModuleManagement.getMetadata(sw1.getId());
|
||||
final List<SoftwareModuleMetadata> metadataSw2 = softwareModuleManagement.getMetadata(sw2.getId());
|
||||
final Map<String, MetadataValue> metadataSw1 = softwareModuleManagement.getMetadata(sw1.getId());
|
||||
final Map<String, MetadataValue> metadataSw2 = softwareModuleManagement.getMetadata(sw2.getId());
|
||||
assertThat(metadataSw1).hasSize(metadataCountSw1);
|
||||
assertThat(metadataSw2).hasSize(metadataCountSw2);
|
||||
|
||||
final Page<SoftwareModuleMetadata> metadataSw1V = softwareModuleManagement.findMetaDataBySoftwareModuleIdAndTargetVisible(
|
||||
sw1.getId(), PAGE_REQUEST_100);
|
||||
final Page<SoftwareModuleMetadata> metadataSw2V = softwareModuleManagement.findMetaDataBySoftwareModuleIdAndTargetVisible(
|
||||
sw2.getId(), PAGE_REQUEST_100);
|
||||
assertThat(metadataSw1V.getNumberOfElements()).isEqualTo(metadataCountSw1);
|
||||
assertThat(metadataSw1V.getTotalElements()).isEqualTo(metadataCountSw1);
|
||||
assertThat(metadataSw2V.getNumberOfElements()).isZero();
|
||||
assertThat(metadataSw2V.getTotalElements()).isZero();
|
||||
final Map<String, String> metadataSw1V = softwareModuleManagement
|
||||
.findMetaDataBySoftwareModuleIdsAndTargetVisible(List.of(sw1.getId())).get(sw1.getId());
|
||||
final Map<String, String> metadataSw2V = softwareModuleManagement
|
||||
.findMetaDataBySoftwareModuleIdsAndTargetVisible(List.of(sw2.getId())).get(sw2.getId());
|
||||
assertThat(metadataSw1V).hasSize(metadataCountSw1);
|
||||
assertThat(metadataSw2V).isNull();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,10 +13,10 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.eclipse.hawkbit.repository.SoftwareModuleFields;
|
||||
import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
|
||||
import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataCreate;
|
||||
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule.MetadataValueCreate;
|
||||
import org.eclipse.hawkbit.repository.test.util.TestdataFactory;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -49,13 +49,8 @@ class RsqlSoftwareModuleFieldTest extends AbstractJpaIntegrationTest {
|
||||
final JpaSoftwareModule ah2 = (JpaSoftwareModule) softwareModuleManagement.create(
|
||||
SoftwareModuleManagement.Create.builder().type(appType).name("agent-hub2").version("1.0.1").description("agent-hub2").build());
|
||||
|
||||
final SoftwareModuleMetadataCreate softwareModuleMetadata = entityFactory.softwareModuleMetadata()
|
||||
.create(ah.getId()).key("metaKey").value("metaValue");
|
||||
softwareModuleManagement.updateMetadata(softwareModuleMetadata);
|
||||
|
||||
final SoftwareModuleMetadataCreate softwareModuleMetadata2 = entityFactory.softwareModuleMetadata()
|
||||
.create(ah2.getId()).key("metaKey").value("value");
|
||||
softwareModuleManagement.updateMetadata(softwareModuleMetadata2);
|
||||
softwareModuleManagement.createMetadata(ah.getId(), "metaKey", new MetadataValueCreate("metaValue"));
|
||||
softwareModuleManagement.createMetadata(ah2.getId(), "metaKey", new MetadataValueCreate("value"));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -160,7 +155,6 @@ class RsqlSoftwareModuleFieldTest extends AbstractJpaIntegrationTest {
|
||||
assertRSQLQuery(SoftwareModuleFields.METADATA.name() + ".metaKey=in=(metaValue,value)", 2);
|
||||
assertRSQLQuery(SoftwareModuleFields.METADATA.name() + ".metaKey=out=(metaValue,notexist)", 1);
|
||||
assertRSQLQuery(SoftwareModuleFields.METADATA.name() + ".notExist==metaValue", 0);
|
||||
|
||||
}
|
||||
|
||||
private void assertRSQLQuery(final String rsql, final long expectedEntity) {
|
||||
|
||||
@@ -18,8 +18,10 @@ import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.PersistenceContext;
|
||||
|
||||
import org.eclipse.hawkbit.repository.RsqlQueryField;
|
||||
import org.eclipse.hawkbit.repository.SoftwareModuleFields;
|
||||
import org.eclipse.hawkbit.repository.TargetFields;
|
||||
import org.eclipse.hawkbit.repository.jpa.JpaRepositoryConfiguration;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
import org.eclipse.hawkbit.repository.jpa.ql.utils.HawkbitQlToSql;
|
||||
import org.eclipse.hawkbit.repository.test.TestConfiguration;
|
||||
@@ -45,6 +47,8 @@ class RsqlToSqlTest {
|
||||
|
||||
@Test
|
||||
void printPG() {
|
||||
printFrom(JpaSoftwareModule.class, SoftwareModuleFields.class, "metadata.x!=y");
|
||||
printFrom(JpaSoftwareModule.class, SoftwareModuleFields.class, "metadata.x==y");
|
||||
printFrom(JpaTarget.class, TargetFields.class, "metadata.x==y");
|
||||
printFrom(JpaTarget.class, TargetFields.class, "tag!=TAG1 and tag==TAG2");
|
||||
printFrom(JpaTarget.class, TargetFields.class, "tag==TAG1 and tag!=TAG2");
|
||||
|
||||
@@ -74,7 +74,7 @@ import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupSuccessCond
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroupConditionBuilder;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroupConditions;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule.MetadataValueCreate;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||
import org.eclipse.hawkbit.repository.model.Tag;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
@@ -341,12 +341,12 @@ public class TestdataFactory {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds {@link SoftwareModuleMetadata} to every module of given {@link DistributionSet}.
|
||||
* Adds software module metadata to every module of given {@link DistributionSet}.
|
||||
* <p/>
|
||||
* {@link #VISIBLE_SM_MD_VALUE}, {@link #VISIBLE_SM_MD_KEY} with
|
||||
* {@link SoftwareModuleMetadata#isTargetVisible()} and
|
||||
* {@link MetadataValueCreate#isTargetVisible()} and
|
||||
* {@link #INVISIBLE_SM_MD_KEY}, {@link #INVISIBLE_SM_MD_VALUE} without
|
||||
* {@link SoftwareModuleMetadata#isTargetVisible()}
|
||||
* {@link MetadataValueCreate#isTargetVisible()}
|
||||
*
|
||||
* @param set to add metadata to
|
||||
*/
|
||||
@@ -1256,10 +1256,14 @@ public class TestdataFactory {
|
||||
}
|
||||
|
||||
private void addTestModuleMetadata(final SoftwareModule module) {
|
||||
softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().create(module.getId())
|
||||
.key(VISIBLE_SM_MD_KEY).value(VISIBLE_SM_MD_VALUE).targetVisible(true));
|
||||
softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().create(module.getId())
|
||||
.key(INVISIBLE_SM_MD_KEY).value(INVISIBLE_SM_MD_VALUE).targetVisible(false));
|
||||
softwareModuleManagement.createMetadata(
|
||||
module.getId(),
|
||||
VISIBLE_SM_MD_KEY,
|
||||
new MetadataValueCreate(VISIBLE_SM_MD_VALUE, true));
|
||||
softwareModuleManagement.createMetadata(
|
||||
module.getId(),
|
||||
INVISIBLE_SM_MD_KEY,
|
||||
new MetadataValueCreate(INVISIBLE_SM_MD_VALUE, false));
|
||||
}
|
||||
|
||||
private void assertTargetProperlyCreated(final Target target) {
|
||||
|
||||
Reference in New Issue
Block a user