Feature target type entity (#1162)

* Added Target Type model

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Added Target Type JPA model

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Added Target Type repository model classes

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Removed the name entity from Target Type

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Refactored the Target Type models

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Added the DB migration script and updated the Target Type models

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Added target type in target Mapper

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Changed the target type ID to Long

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Added MYSQL DB migration script and removed the deleted column for target type

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Updated the DB migration script for target table

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Added manyToMany reltation between target type and Ds type

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Added POSTGRESQL DB migration script

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Added MSSQL SERVER DB migration script

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Added DB2 DB migration script

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Added missing license header and java docs

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Added on delete cascade in DB migration script

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Added Target Type specification

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Removed the delete cascade and Added type API
Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* fixed API doc build

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Added target type management test

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Added target type events test

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Added target type update and unassign to target

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Added API tests for assigning target type to target

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Added missing license header

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Added missing docs

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Fixed sonar issues

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Fixed license header build issue

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Updated the attribute name to target type

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Fixed the review comments

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Removed unused error status variable

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Added target API to assign target type

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Added the tests for assigning target type to target

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Fixed the review comments for null check

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>
This commit is contained in:
Anand Kumar
2021-08-25 12:13:27 +02:00
committed by GitHub
parent 2574581b2c
commit 3fa1dd1be4
76 changed files with 4915 additions and 41 deletions

View File

@@ -22,6 +22,7 @@ import org.eclipse.hawkbit.repository.builder.SoftwareModuleTypeBuilder;
import org.eclipse.hawkbit.repository.builder.TagBuilder;
import org.eclipse.hawkbit.repository.builder.TargetBuilder;
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryBuilder;
import org.eclipse.hawkbit.repository.builder.TargetTypeBuilder;
import org.eclipse.hawkbit.repository.model.BaseEntity;
import org.eclipse.hawkbit.repository.model.MetaData;
@@ -109,6 +110,11 @@ public interface EntityFactory {
*/
TargetBuilder target();
/**
* @return {@link TargetTypeBuilder} object
*/
TargetTypeBuilder targetType();
/**
* @return {@link TargetFilterQueryBuilder} object
*/

View File

@@ -106,4 +106,9 @@ public interface QuotaManagement {
*/
long getMaxArtifactStorage();
/**
* @return the maximum number of distribution set types per target type
*/
int getMaxDistributionSetTypesPerTargetType();
}

View File

@@ -34,6 +34,7 @@ import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.repository.model.TargetMetadata;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.model.TargetTagAssignmentResult;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.springframework.data.domain.Page;
@@ -598,6 +599,30 @@ public interface TargetManagement {
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
Target unAssignTag(@NotEmpty String controllerID, long targetTagId);
/**
* Un-assign a {@link TargetType} assignment to given {@link Target}.
*
* @param controllerID
* to un-assign for
* @return the unassigned target
*
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
Target unAssignType(@NotEmpty String controllerID);
/**
* Assign a {@link TargetType} assignment to given {@link Target}.
*
* @param controllerID
* to un-assign for
* @return the unassigned target
*
* @throws EntityNotFoundException
* if TargetType with given target ID does not exist
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
Target assignType(@NotEmpty String controllerID, @NotNull long targetTypeId);
/**
* updates the {@link Target}.
*

View File

@@ -0,0 +1,166 @@
/**
* Copyright (c) 2021 Bosch.IO GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.repository.builder.TargetTypeCreate;
import org.eclipse.hawkbit.repository.builder.TargetTypeUpdate;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.security.access.prepost.PreAuthorize;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
/**
* Management service for {@link TargetType}s.
*
*/
public interface TargetTypeManagement {
/**
* @param name
* as {@link TargetType#getName()}
* @return {@link TargetType}
*/
@PreAuthorize(SpPermission.SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Optional<TargetType> getByName(@NotEmpty String name);
/**
* @return total count
*/
@PreAuthorize(SpPermission.SpringEvalExpressions.HAS_AUTH_READ_TARGET)
long count();
/**
* @param create
* TargetTypeCreate
* @return targetType
*/
@PreAuthorize(SpPermission.SpringEvalExpressions.HAS_AUTH_CREATE_TARGET)
TargetType create(@NotNull @Valid TargetTypeCreate create);
/**
* @param creates
* List of TargetTypeCreate
* @return List of targetType
*/
@PreAuthorize(SpPermission.SpringEvalExpressions.HAS_AUTH_CREATE_TARGET)
List<TargetType> create(@NotEmpty @Valid Collection<TargetTypeCreate> creates);
/**
* @param id targetTypeId
*/
@PreAuthorize(SpPermission.SpringEvalExpressions.HAS_AUTH_DELETE_TARGET)
void delete(@NotNull Long id);
/**
* @param pageable
* Page
* @return TargetType page
*/
@PreAuthorize(SpPermission.SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Page<TargetType> findAll(@NotNull Pageable pageable);
/**
* @param pageable
* Page
* @param rsqlParam
* query param
* @return Target type
*/
@PreAuthorize(SpPermission.SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Page<TargetType> findByRsql(@NotNull Pageable pageable, @NotEmpty String rsqlParam);
/**
* @param id
* Target type ID
* @return Target Type
*/
@PreAuthorize(SpPermission.SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Optional<TargetType> get(long id);
/**
* @param targetId
* Target ID
* @return Target Type
*/
@PreAuthorize(SpPermission.SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Optional<TargetType> findByTargetId(long targetId);
/**
* @param targetIds
* List of Target ID
* @return Target Type
*/
@PreAuthorize(SpPermission.SpringEvalExpressions.HAS_AUTH_READ_TARGET)
List<TargetType> findByTargetIds(Collection<Long> targetIds);
/**
* @param controllerId
* Target controller ID
* @return Target Type
*/
@PreAuthorize(SpPermission.SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Optional<TargetType> findByTargetControllerId(String controllerId);
/**
* @param controllerIds
* List of Target controller ID
* @return Target Type
*/
@PreAuthorize(SpPermission.SpringEvalExpressions.HAS_AUTH_READ_TARGET)
List<TargetType> findByTargetControllerIds(Collection<String> controllerIds);
/**
* @param ids
* List of Target type ID
* @return Target type list
*/
@PreAuthorize(SpPermission.SpringEvalExpressions.HAS_AUTH_READ_TARGET)
List<TargetType> get(@NotEmpty Collection<Long> ids);
/**
* @param update
* TargetTypeUpdate
* @return Target Type
*/
@PreAuthorize(SpPermission.SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
TargetType update(@NotNull @Valid TargetTypeUpdate update);
/**
* @param targetTypeId
* Target type ID
* @param distributionSetTypeIds
* Distribution set ID
* @return Target type
*/
@PreAuthorize(SpPermission.SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
TargetType assignCompatibleDistributionSetTypes(long targetTypeId,
@NotEmpty Collection<Long> distributionSetTypeIds);
/**
* @param targetTypeId
* Target type ID
* @param distributionSetTypeIds
* Distribution set ID
* @return Target type
*/
@PreAuthorize(SpPermission.SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
TargetType unassignDistributionSetType(long targetTypeId, long distributionSetTypeIds);
}

View File

@@ -46,6 +46,13 @@ public interface TargetCreate {
*/
TargetCreate description(@Size(max = NamedEntity.DESCRIPTION_MAX_SIZE) String description);
/**
* @param targetTypeId
* for {@link Target#getTargetType()}
* @return updated builder instance
*/
TargetCreate targetType(Long targetTypeId);
/**
* @param securityToken
* for {@link Target#getSecurityToken()} is generated with a

View File

@@ -0,0 +1,29 @@
/**
* Copyright (c) 2021 Bosch.IO GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.builder;
import org.eclipse.hawkbit.repository.model.TargetType;
/**
* Builder for {@link TargetType}.
*
*/
public interface TargetTypeBuilder {
/**
* @param id
* of the updatable entity
* @return builder instance
*/
TargetTypeUpdate update(long id);
/**
* @return builder instance
*/
TargetTypeCreate create();
}

View File

@@ -0,0 +1,81 @@
/**
* Copyright (c) 2021 Bosch.IO GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.builder;
import org.eclipse.hawkbit.repository.model.BaseEntity;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.NamedEntity;
import org.eclipse.hawkbit.repository.model.TargetType;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.Collection;
import java.util.Collections;
/**
* Builder to create a new {@link TargetType} 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 TargetTypeCreate {
/**
* @param name
* for {@link TargetType#getName()}
* @return updated builder instance
*/
TargetTypeCreate name(@Size(min = 1, max = NamedEntity.NAME_MAX_SIZE) @NotEmpty String name);
/**
* @param description
* for {@link TargetType#getDescription()}
* @return updated builder instance
*/
TargetTypeCreate description(@Size(max = NamedEntity.DESCRIPTION_MAX_SIZE) String description);
/**
* @param colour
* for {@link TargetType#getColour()}
* @return updated builder instance
*/
TargetTypeCreate colour(@Size(max = TargetType.COLOUR_MAX_SIZE) String colour);
/**
* @param compatible
* for {@link TargetType#getCompatibleDistributionSetTypes()}
* @return updated builder instance
*/
TargetTypeCreate compatible(@NotEmpty Collection<Long> compatible);
/**
* @param compatible
* for {@link TargetType#getCompatibleDistributionSetTypes()}
* @return updated builder instance
*/
default TargetTypeCreate compatible(@NotNull final Long compatible) {
return compatible(Collections.singletonList(compatible));
}
/**
* @param compatible
* for {@link TargetType#getCompatibleDistributionSetTypes()}
* @return updated builder instance
*/
default TargetTypeCreate compatible(@NotNull final DistributionSetType compatible) {
return compatible(compatible.getId());
}
/**
* @return peek on current state of {@link TargetType} in the
* builder
*/
TargetType build();
}

View File

@@ -0,0 +1,42 @@
/**
* Copyright (c) 2021 Bosch.IO GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.builder;
import org.eclipse.hawkbit.repository.model.NamedEntity;
import org.eclipse.hawkbit.repository.model.TargetType;
import javax.validation.constraints.Size;
/**
* Builder to update an existing {@link TargetType} entry. Defines all
* fields that can be updated.
*
*/
public interface TargetTypeUpdate {
/**
* @param description
* for {@link TargetType#getDescription()}
* @return updated builder instance
*/
TargetTypeUpdate description(@Size(max = NamedEntity.DESCRIPTION_MAX_SIZE) String description);
/**
* @param colour
* for {@link TargetType#getColour()}
* @return updated builder instance
*/
TargetTypeUpdate colour(@Size(max = TargetType.COLOUR_MAX_SIZE) String colour);
/**
* @param name
* Name
* @return updated builder instance
*/
TargetTypeUpdate name(@Size(max = TargetType.NAME_MAX_SIZE) String name);
}

View File

@@ -43,6 +43,13 @@ public interface TargetUpdate {
*/
TargetUpdate securityToken(@Size(min = 1, max = Target.SECURITY_TOKEN_MAX_SIZE) @NotNull String securityToken);
/**
* @param targetTypeId
* for {@link Target#getTargetType()}
* @return updated builder instance
*/
TargetUpdate targetType(@NotNull Long targetTypeId);
/**
* @param address
* for {@link Target#getAddress()}

View File

@@ -0,0 +1,46 @@
/**
* Copyright (c) 2021 Bosch.IO GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.event.remote;
import org.eclipse.hawkbit.repository.event.entity.EntityDeletedEvent;
import org.eclipse.hawkbit.repository.model.TargetType;
/**
*
* Defines the remote event of deleting a {@link TargetType}.
*/
public class TargetTypeDeletedEvent extends RemoteIdEvent implements EntityDeletedEvent {
private static final long serialVersionUID = 1L;
/**
* Default constructor.
*/
public TargetTypeDeletedEvent() {
// for serialization libs like jackson
}
/**
* Constructor for json serialization.
*
* @param tenant
* the tenant
* @param entityId
* the entity id
* @param entityClass
* the entity class
* @param applicationId
* the origin application id
*/
public TargetTypeDeletedEvent(final String tenant, final Long entityId, final String entityClass,
final String applicationId) {
super(entityId, tenant, entityClass, applicationId);
}
}

View File

@@ -0,0 +1,41 @@
/**
* Copyright (c) 2021 Bosch.IO GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.event.remote.entity;
import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent;
import org.eclipse.hawkbit.repository.model.TargetType;
/**
* Defines the remote event of creating a new {@link TargetType}.
*
*/
public class TargetTypeCreatedEvent extends RemoteEntityEvent<TargetType>
implements EntityCreatedEvent {
private static final long serialVersionUID = 1L;
/**
* Default constructor.
*/
public TargetTypeCreatedEvent() {
// for serialization libs like jackson
}
/**
* Constructor.
*
* @param baseEntity
* the TargetType
* @param applicationId
* the origin application id
*/
public TargetTypeCreatedEvent(final TargetType baseEntity, final String applicationId) {
super(baseEntity, applicationId);
}
}

View File

@@ -0,0 +1,42 @@
/**
* Copyright (c) 2021 Bosch.IO GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.event.remote.entity;
import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent;
import org.eclipse.hawkbit.repository.model.TargetType;
/**
* Defines the remote event for updating a {@link TargetType}.
*
*/
public class TargetTypeUpdatedEvent extends RemoteEntityEvent<TargetType>
implements EntityUpdatedEvent {
private static final long serialVersionUID = 1L;
/**
* Default constructor.
*/
public TargetTypeUpdatedEvent() {
// for serialization libs like jackson
}
/**
* Constructor.
*
* @param baseEntity
* TargetType
* @param applicationId
* the origin application id
*/
public TargetTypeUpdatedEvent(final TargetType baseEntity, final String applicationId) {
super(baseEntity, applicationId);
}
}

View File

@@ -0,0 +1,37 @@
/**
* Copyright (c) 2021 Bosch.IO GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.exception;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.TargetType;
/**
* the {@link DistributionSetTypeNotInTargetTypeException} is thrown when a
* {@link DistributionSetType} is requested as part of a {@link TargetType} but
* is not returned in {@link TargetType#getCompatibleDistributionSetTypes()}.
*/
public class DistributionSetTypeNotInTargetTypeException extends EntityNotFoundException {
private static final long serialVersionUID = 1L;
/**
* Constructor
*
* @param distributionSetTypeId
* that is not part of given {@link TargetType}
* @param targetTypeId
* of the {@link TargetType} where given
* {@link DistributionSetType} is not part of
*/
public DistributionSetTypeNotInTargetTypeException(final Long distributionSetTypeId,
final Long targetTypeId) {
super(DistributionSetType.class.getSimpleName() + " with id {" + distributionSetTypeId + "} is not part of "
+ TargetType.class.getSimpleName() + " with id {" + targetTypeId + "}.");
}
}

View File

@@ -0,0 +1,61 @@
/**
* Copyright (c) 2021 Bosch.IO GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.exception;
import org.eclipse.hawkbit.exception.AbstractServerRtException;
import org.eclipse.hawkbit.exception.SpServerError;
/**
* Thrown if target type is assigned
*/
public class TargetTypeInUseException extends AbstractServerRtException {
private static final long serialVersionUID = 1L;
private static final SpServerError THIS_ERROR = SpServerError.SP_TARGET_TYPE_IN_USE;
/**
* Default constructor.
*/
public TargetTypeInUseException() {
super(THIS_ERROR);
}
/**
* Parameterized constructor.
*
* @param cause
* of the exception
*/
public TargetTypeInUseException(final Throwable cause) {
super(THIS_ERROR, cause);
}
/**
* Parameterized constructor.
*
* @param message
* of the exception
* @param cause
* of the exception
*/
public TargetTypeInUseException(final String message, final Throwable cause) {
super(message, THIS_ERROR, cause);
}
/**
* Parameterized constructor.
*
* @param message
* of the exception
*/
public TargetTypeInUseException(final String message) {
super(message, THIS_ERROR);
}
}

View File

@@ -85,6 +85,11 @@ public interface Target extends NamedEntity {
*/
TargetUpdateStatus getUpdateStatus();
/**
* @return Target type {@link TargetType}.
*/
TargetType getTargetType();
/**
* @return the poll time which holds the last poll time of the target, the
* next poll time and the overdue time. In case the

View File

@@ -0,0 +1,69 @@
/**
* Copyright (c) 2021 Bosch.IO GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.model;
import java.util.Set;
/**
* A {@link TargetType} is an abstract definition for
* {@link Target}
*
*/
public interface TargetType extends NamedEntity {
/**
* Maximum length of color in Management UI.
*/
int COLOUR_MAX_SIZE = 16;
/**
* @return immutable set of optional {@link DistributionSetType}s
*/
Set<DistributionSetType> getCompatibleDistributionSetTypes();
/**
* @return immutable set of optional {@link Target}s
*/
Set<Target> getTargets();
/**
* Checks if the given {@link DistributionSetType} is in
* {@link #getCompatibleDistributionSetTypes()}.
*
* @param distributionSetType
* search for
* @return <code>true</code> if found
*/
default boolean containsCompatibleDistributionSetType(final DistributionSetType distributionSetType) {
return containsCompatibleDistributionSetType(distributionSetType.getId());
}
/**
* Checks if the given {@link DistributionSetType} is in
* {@link #getCompatibleDistributionSetTypes()}.
*
* @param distributionSetTypeId
* search by {@link DistributionSetType#getId()}
* @return <code>true</code> if found
*/
default boolean containsCompatibleDistributionSetType(final Long distributionSetTypeId) {
return getCompatibleDistributionSetTypes().stream().anyMatch(element -> element.getId().equals(distributionSetTypeId));
}
/**
* Unassigns a distribution set type from target types
* @param dsTypeId
* @return the resulting target type
*/
TargetType removeDistributionSetType(final Long dsTypeId);
/**
* @return get color code to be used in management UI views.
*/
String getColour();
}

View File

@@ -108,4 +108,8 @@ public class PropertiesQuotaManagement implements QuotaManagement {
return securityProperties.getDos().getMaxArtifactStorage();
}
@Override
public int getMaxDistributionSetTypesPerTargetType() {
return securityProperties.getDos().getMaxDistributionSetTypesPerTargetType();
}
}

View File

@@ -0,0 +1,63 @@
/**
* Copyright (c) 2021 Bosch.IO GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.builder;
import org.eclipse.hawkbit.repository.ValidString;
import org.springframework.util.StringUtils;
import java.util.Collection;
import java.util.Optional;
/**
* Create and update builder DTO.
*
* @param <T>
* update or create builder interface
*/
public abstract class AbstractTargetTypeUpdateCreate<T> extends AbstractNamedEntityBuilder<T> {
@ValidString
protected String colour;
protected Collection<Long> compatible;
/**
* @param compatible
* list of ID
* @return generic type
*/
public T compatible(final Collection<Long> compatible) {
this.compatible = compatible;
return (T) this;
}
/**
* @return List of ID
*/
public Optional<Collection<Long>> getCompatible() {
return Optional.ofNullable(compatible);
}
/**
* @param colour
* Colour value
* @return generic type
*/
public T colour(final String colour) {
this.colour = StringUtils.trimWhitespace(colour);
return (T) this;
}
/**
* @return colour
*/
public Optional<String> getColour() {
return Optional.ofNullable(colour);
}
}

View File

@@ -11,8 +11,11 @@ package org.eclipse.hawkbit.repository.builder;
import java.net.URI;
import java.util.Optional;
import org.eclipse.hawkbit.repository.TargetTypeManagement;
import org.eclipse.hawkbit.repository.ValidString;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.InvalidTargetAddressException;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.springframework.util.StringUtils;
@@ -36,6 +39,8 @@ public class AbstractTargetUpdateCreate<T> extends AbstractNamedEntityBuilder<T>
protected Boolean requestAttributes;
protected Long targetTypeId;
protected AbstractTargetUpdateCreate(final String controllerId) {
this.controllerId = StringUtils.trimWhitespace(controllerId);
}
@@ -79,6 +84,11 @@ public class AbstractTargetUpdateCreate<T> extends AbstractNamedEntityBuilder<T>
return (TargetCreate) this;
}
public T targetType(final Long targetTypeId) {
this.targetTypeId = targetTypeId;
return (T) this;
}
public String getControllerId() {
return controllerId;
}
@@ -99,4 +109,8 @@ public class AbstractTargetUpdateCreate<T> extends AbstractNamedEntityBuilder<T>
return Optional.ofNullable(status);
}
public Long getTargetTypeId() {
return targetTypeId;
}
}

View File

@@ -0,0 +1,25 @@
/**
* Copyright (c) 2021 Bosch.IO GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.builder;
/**
* Update implementation.
*/
public class GenericTargetTypeUpdate extends AbstractTargetTypeUpdateCreate<TargetTypeUpdate>
implements TargetTypeUpdate {
/**
* @param id
* Target type ID
*/
public GenericTargetTypeUpdate(final Long id) {
super.id = id;
}
}

View File

@@ -32,6 +32,7 @@ import org.eclipse.hawkbit.repository.jpa.builder.JpaDistributionSetTypeCreate;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.jpa.specifications.DistributionSetTypeSpecification;
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
@@ -66,6 +67,8 @@ public class JpaDistributionSetTypeManagement implements DistributionSetTypeMana
private final DistributionSetRepository distributionSetRepository;
private final TargetTypeRepository targetTypeRepository;
private final VirtualPropertyReplacer virtualPropertyReplacer;
private final Database database;
@@ -74,12 +77,13 @@ public class JpaDistributionSetTypeManagement implements DistributionSetTypeMana
JpaDistributionSetTypeManagement(final DistributionSetTypeRepository distributionSetTypeRepository,
final SoftwareModuleTypeRepository softwareModuleTypeRepository,
final DistributionSetRepository distributionSetRepository,
final DistributionSetRepository distributionSetRepository, final TargetTypeRepository targetTypeRepository,
final VirtualPropertyReplacer virtualPropertyReplacer, final Database database,
final QuotaManagement quotaManagement) {
this.distributionSetTypeRepository = distributionSetTypeRepository;
this.softwareModuleTypeRepository = softwareModuleTypeRepository;
this.distributionSetRepository = distributionSetRepository;
this.targetTypeRepository = targetTypeRepository;
this.virtualPropertyReplacer = virtualPropertyReplacer;
this.database = database;
this.quotaManagement = quotaManagement;
@@ -190,7 +194,7 @@ public class JpaDistributionSetTypeManagement implements DistributionSetTypeMana
}
/**
* Enforces the quota specifiying the maximum number of
* Enforces the quota specifying the maximum number of
* {@link SoftwareModuleType}s per {@link DistributionSetType}.
*
* @param id
@@ -268,18 +272,28 @@ public class JpaDistributionSetTypeManagement implements DistributionSetTypeMana
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void delete(final long typeId) {
final JpaDistributionSetType toDelete = distributionSetTypeRepository.findById(typeId)
.orElseThrow(() -> new EntityNotFoundException(DistributionSetType.class, typeId));
unassignDsTypeFromTargetTypes(typeId);
if (distributionSetRepository.countByTypeId(typeId) > 0) {
toDelete.setDeleted(true);
distributionSetTypeRepository.save(toDelete);
} else {
}
else {
distributionSetTypeRepository.deleteById(typeId);
}
}
private void unassignDsTypeFromTargetTypes(long typeId) {
List<JpaTargetType> targetTypesByDsType = targetTypeRepository.findByDsType(typeId);
targetTypesByDsType.forEach(targetType -> {
targetType.removeDistributionSetType(typeId);
targetTypeRepository.save(targetType);
});
}
@Override
@Transactional
@Retryable(include = {

View File

@@ -20,6 +20,7 @@ import org.eclipse.hawkbit.repository.builder.SoftwareModuleTypeBuilder;
import org.eclipse.hawkbit.repository.builder.TagBuilder;
import org.eclipse.hawkbit.repository.builder.TargetBuilder;
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryBuilder;
import org.eclipse.hawkbit.repository.builder.TargetTypeBuilder;
import org.eclipse.hawkbit.repository.jpa.builder.JpaActionStatusBuilder;
import org.eclipse.hawkbit.repository.jpa.builder.JpaRolloutGroupBuilder;
import org.eclipse.hawkbit.repository.jpa.builder.JpaSoftwareModuleTypeBuilder;
@@ -42,6 +43,9 @@ public class JpaEntityFactory implements EntityFactory {
@Autowired
private DistributionSetBuilder distributionSetBuilder;
@Autowired
private TargetBuilder targetBuilder;
@Autowired
private DistributionSetTypeBuilder distributionSetTypeBuilder;
@@ -57,6 +61,9 @@ public class JpaEntityFactory implements EntityFactory {
@Autowired
private SoftwareModuleMetadataBuilder softwareModuleMetadataBuilder;
@Autowired
private TargetTypeBuilder targetTypeBuilder;
@Override
public MetaData generateDsMetadata(final String key, final String value) {
return new JpaDistributionSetMetadata(key, StringUtils.trimWhitespace(value));
@@ -79,7 +86,12 @@ public class JpaEntityFactory implements EntityFactory {
@Override
public TargetBuilder target() {
return new JpaTargetBuilder();
return targetBuilder;
}
@Override
public TargetTypeBuilder targetType() {
return targetTypeBuilder;
}
@Override

View File

@@ -25,6 +25,7 @@ import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.configuration.MultiTenantJpaTransactionManager;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType;
import org.eclipse.hawkbit.repository.jpa.model.JpaTenantMetaData;
import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
@@ -93,6 +94,9 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
@Autowired
private TargetTagRepository targetTagRepository;
@Autowired
private TargetTypeRepository targetTypeRepository;
@Autowired
private DistributionSetTagRepository distributionSetTagRepository;
@@ -255,6 +259,7 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
targetRepository.deleteByTenant(tenant);
targetFilterQueryRepository.deleteByTenant(tenant);
rolloutRepository.deleteByTenant(tenant);
targetTypeRepository.deleteByTenant(tenant);
targetTagRepository.deleteByTenant(tenant);
distributionSetTagRepository.deleteByTenant(tenant);
distributionSetRepository.deleteByTenant(tenant);

View File

@@ -49,6 +49,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetMetadata;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetMetadata_;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
import org.eclipse.hawkbit.repository.jpa.model.TargetMetadataCompositeKey;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
@@ -63,6 +64,7 @@ import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.repository.model.TargetMetadata;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.model.TargetTagAssignmentResult;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
@@ -98,6 +100,8 @@ public class JpaTargetManagement implements TargetManagement {
private final TargetRepository targetRepository;
private final TargetTypeRepository targetTypeRepository;
private final TargetMetadataRepository targetMetadataRepository;
private final RolloutGroupRepository rolloutGroupRepository;
@@ -119,7 +123,7 @@ public class JpaTargetManagement implements TargetManagement {
private final Database database;
public JpaTargetManagement(final EntityManager entityManager, final QuotaManagement quotaManagement,
final TargetRepository targetRepository, final TargetMetadataRepository targetMetadataRepository,
final TargetRepository targetRepository, final TargetTypeRepository targetTypeRepository, final TargetMetadataRepository targetMetadataRepository,
final RolloutGroupRepository rolloutGroupRepository,
final DistributionSetRepository distributionSetRepository,
final TargetFilterQueryRepository targetFilterQueryRepository,
@@ -130,6 +134,7 @@ public class JpaTargetManagement implements TargetManagement {
this.entityManager = entityManager;
this.quotaManagement = quotaManagement;
this.targetRepository = targetRepository;
this.targetTypeRepository = targetTypeRepository;
this.targetMetadataRepository = targetMetadataRepository;
this.rolloutGroupRepository = rolloutGroupRepository;
this.distributionSetRepository = distributionSetRepository;
@@ -152,6 +157,10 @@ public class JpaTargetManagement implements TargetManagement {
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
}
private JpaTargetType getTargetTypeByIdAndThrowIfNotFound(final long id) {
return targetTypeRepository.findById(id).orElseThrow(() -> new EntityNotFoundException(TargetType.class, id));
}
@Override
public List<Target> getByControllerID(final Collection<String> controllerIDs) {
return Collections.unmodifiableList(
@@ -333,6 +342,10 @@ public class JpaTargetManagement implements TargetManagement {
update.getDescription().ifPresent(target::setDescription);
update.getAddress().ifPresent(target::setAddress);
update.getSecurityToken().ifPresent(target::setSecurityToken);
if (update.getTargetTypeId() != null){
TargetType targetType = getTargetTypeByIdAndThrowIfNotFound(update.getTargetTypeId());
target.setTargetType(targetType);
}
return targetRepository.save(target);
}
@@ -589,6 +602,24 @@ public class JpaTargetManagement implements TargetManagement {
return result;
}
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Target unAssignType(final String controllerID) {
final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerID);
target.setTargetType(null);
return targetRepository.save(target);
}
@Override
public Target assignType(String controllerID, long targetTypeId) {
final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerID);
final JpaTargetType targetType = getTargetTypeByIdAndThrowIfNotFound(targetTypeId);
target.setTargetType(targetType);
return targetRepository.save(target);
}
@Override
public Slice<Target> findByFilterOrderByLinkedDistributionSet(final Pageable pageable,
final long orderByDistributionId, final FilterParams filterParams) {

View File

@@ -0,0 +1,250 @@
/**
* Copyright (c) 2021 Bosch.IO GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.jpa;
import org.eclipse.hawkbit.repository.QuotaManagement;
import org.eclipse.hawkbit.repository.TargetTypeFields;
import org.eclipse.hawkbit.repository.TargetTypeManagement;
import org.eclipse.hawkbit.repository.builder.GenericTargetTypeUpdate;
import org.eclipse.hawkbit.repository.builder.TargetTypeCreate;
import org.eclipse.hawkbit.repository.builder.TargetTypeUpdate;
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.TargetTypeInUseException;
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetTypeCreate;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetTypeSpecification;
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
/**
* JPA implementation of {@link TargetTypeManagement}.
*
*/
@Transactional(readOnly = true)
@Validated
public class JpaTargetTypeManagement implements TargetTypeManagement {
private final TargetTypeRepository targetTypeRepository;
private final TargetRepository targetRepository;
private final DistributionSetTypeRepository distributionSetTypeRepository;
private final VirtualPropertyReplacer virtualPropertyReplacer;
private final Database database;
private final QuotaManagement quotaManagement;
/**
* Constructor
*
* @param targetTypeRepository
* Target type repository
* @param targetRepository
* Target repository
* @param virtualPropertyReplacer
* replacer
* @param database
* database
*/
public JpaTargetTypeManagement(final TargetTypeRepository targetTypeRepository,
final TargetRepository targetRepository, final DistributionSetTypeRepository distributionSetTypeRepository,
final VirtualPropertyReplacer virtualPropertyReplacer, final Database database,
final QuotaManagement quotaManagement) {
this.targetTypeRepository = targetTypeRepository;
this.targetRepository = targetRepository;
this.distributionSetTypeRepository = distributionSetTypeRepository;
this.virtualPropertyReplacer = virtualPropertyReplacer;
this.database = database;
this.quotaManagement = quotaManagement;
}
@Override
public Optional<TargetType> getByName(String name) {
return targetTypeRepository.findByName(name).map(TargetType.class::cast);
}
@Override
public long count() {
return targetTypeRepository.count();
}
@Override
public TargetType create(TargetTypeCreate create) {
final JpaTargetTypeCreate typeCreate = (JpaTargetTypeCreate) create;
return targetTypeRepository.save(typeCreate.build());
}
@Override
public List<TargetType> create(Collection<TargetTypeCreate> creates) {
return creates.stream().map(this::create).collect(Collectors.toList());
}
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void delete(final Long targetTypeId) {
throwExceptionIfTargetTypeDoesNotExist(targetTypeId);
if (targetRepository.countByTargetTypeId(targetTypeId) > 0) {
throw new TargetTypeInUseException("Cannot delete target type that is in use");
}
targetTypeRepository.deleteById(targetTypeId);
}
@Override
public Page<TargetType> findAll(Pageable pageable) {
return convertPage(targetTypeRepository.findAll(pageable), pageable);
}
@Override
public Page<TargetType> findByRsql(Pageable pageable, String rsqlParam) {
final Specification<JpaTargetType> spec = RSQLUtility.buildRsqlSpecification(rsqlParam, TargetTypeFields.class,
virtualPropertyReplacer, database);
return convertPage(targetTypeRepository.findAll(spec, pageable), pageable);
}
@Override
public Optional<TargetType> get(long id) {
return targetTypeRepository.findById(id).map(targetType -> targetType);
}
@Override
public Optional<TargetType> findByTargetId(long targetId) {
return targetTypeRepository
.findOne(TargetTypeSpecification.hasTarget(targetId)).map(TargetType.class::cast);
}
@Override
public List<TargetType> findByTargetIds(Collection<Long> targetIds) {
return targetTypeRepository.findAll(TargetTypeSpecification.hasTarget(targetIds))
.stream().map(TargetType.class::cast).collect(Collectors.toList());
}
@Override
public Optional<TargetType> findByTargetControllerId(String controllerId) {
return targetTypeRepository
.findOne(TargetTypeSpecification.hasTargetControllerId(controllerId)).map(TargetType.class::cast);
}
@Override
public List<TargetType> findByTargetControllerIds(Collection<String> controllerIds) {
return targetTypeRepository.findAll(TargetTypeSpecification.hasTargetControllerIdIn(controllerIds))
.stream().map(TargetType.class::cast).collect(Collectors.toList());
}
@Override
public List<TargetType> get(Collection<Long> ids) {
return Collections.unmodifiableList(targetTypeRepository.findAllById(ids));
}
@Override
public TargetType update(TargetTypeUpdate update) {
final GenericTargetTypeUpdate typeUpdate = (GenericTargetTypeUpdate) update;
final JpaTargetType type = findTargetTypeAndThrowExceptionIfNotFound(typeUpdate.getId());
typeUpdate.getName().ifPresent((type::setName));
typeUpdate.getDescription().ifPresent(type::setDescription);
typeUpdate.getColour().ifPresent(type::setColour);
return targetTypeRepository.save(type);
}
@Override
public TargetType assignCompatibleDistributionSetTypes(long targetTypeId, Collection<Long> distributionSetTypeIds) {
final Collection<JpaDistributionSetType> dsTypes = distributionSetTypeRepository
.findAllById(distributionSetTypeIds);
if (dsTypes.size() < distributionSetTypeIds.size()) {
throw new EntityNotFoundException(DistributionSetType.class, distributionSetTypeIds,
dsTypes.stream().map(DistributionSetType::getId).collect(Collectors.toList()));
}
final JpaTargetType type = findTargetTypeAndThrowExceptionIfNotFound(targetTypeId);
assertDistributionSetTypeQuota(targetTypeId, distributionSetTypeIds.size());
dsTypes.forEach(type::addCompatibleDistributionSetType);
return targetTypeRepository.save(type);
}
@Override
public TargetType unassignDistributionSetType(long targetTypeId, long distributionSetTypeId) {
final JpaTargetType type = findTargetTypeAndThrowExceptionIfNotFound(targetTypeId);
findDsTypeAndThrowExceptionIfNotFound(distributionSetTypeId);
type.removeDistributionSetType(distributionSetTypeId);
return targetTypeRepository.save(type);
}
private JpaTargetType findTargetTypeAndThrowExceptionIfNotFound(final Long typeId) {
return (JpaTargetType) get(typeId).orElseThrow(() -> new EntityNotFoundException(TargetType.class, typeId));
}
private void findDsTypeAndThrowExceptionIfNotFound(final Long typeId) {
distributionSetTypeRepository.findById(typeId).orElseThrow(() -> new EntityNotFoundException(DistributionSetType.class, typeId));
}
private void throwExceptionIfTargetTypeDoesNotExist(final Long typeId) {
if (!targetTypeRepository.existsById(typeId)) {
throw new EntityNotFoundException(TargetType.class, typeId);
}
}
/**
* Enforces the quota specifying the maximum number of
* {@link DistributionSetType}s per {@link TargetType}.
*
* @param id
* of the target type
* @param requested
* number of distribution set types to check
*
* @throws AssignmentQuotaExceededException
* if the software module type quota is exceeded
*/
private void assertDistributionSetTypeQuota(final long id, final int requested) {
QuotaHelper.assertAssignmentQuota(id, requested, quotaManagement.getMaxDistributionSetTypesPerTargetType(),
DistributionSetType.class, TargetType.class, targetTypeRepository::countDsSetTypesById);
}
private static Page<TargetType> convertPage(final Page<JpaTargetType> findAll, final Pageable pageable) {
return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, findAll.getTotalElements());
}
private static Slice<TargetType> convertPage(final Slice<JpaTargetType> findAll, final Pageable pageable) {
return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, 0);
}
}

View File

@@ -38,6 +38,7 @@ import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.TargetTagManagement;
import org.eclipse.hawkbit.repository.TargetTypeManagement;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.TenantStatsManagement;
import org.eclipse.hawkbit.repository.autoassign.AutoAssignExecutor;
@@ -46,7 +47,9 @@ import org.eclipse.hawkbit.repository.builder.DistributionSetTypeBuilder;
import org.eclipse.hawkbit.repository.builder.RolloutBuilder;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleBuilder;
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;
import org.eclipse.hawkbit.repository.event.ApplicationEventFilter;
import org.eclipse.hawkbit.repository.event.remote.EventEntityManager;
import org.eclipse.hawkbit.repository.event.remote.EventEntityManagerHolder;
@@ -62,7 +65,9 @@ import org.eclipse.hawkbit.repository.jpa.builder.JpaDistributionSetTypeBuilder;
import org.eclipse.hawkbit.repository.jpa.builder.JpaRolloutBuilder;
import org.eclipse.hawkbit.repository.jpa.builder.JpaSoftwareModuleBuilder;
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;
import org.eclipse.hawkbit.repository.jpa.configuration.MultiTenantJpaTransactionManager;
import org.eclipse.hawkbit.repository.jpa.event.JpaEventEntityManager;
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitDefaultServiceExecutor;
@@ -85,6 +90,7 @@ import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
import org.eclipse.hawkbit.repository.model.helper.SystemManagementHolder;
import org.eclipse.hawkbit.repository.model.helper.TenantConfigurationManagementHolder;
@@ -231,6 +237,21 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
return new JpaDistributionSetBuilder(distributionSetTypeManagement, softwareManagement);
}
@Bean
TargetBuilder targetBuilder(final TargetTypeManagement targetTypeManagement){
return new JpaTargetBuilder(targetTypeManagement);
}
/**
* @param dsTypeManagement
* for loading {@link TargetType#getCompatibleDistributionSetTypes()}
* @return TargetTypeBuilder bean
*/
@Bean
TargetTypeBuilder targetTypeBuilder(final DistributionSetTypeManagement dsTypeManagement) {
return new JpaTargetTypeBuilder(dsTypeManagement);
}
@Bean
SoftwareModuleMetadataBuilder softwareModuleMetadataBuilder(
final SoftwareModuleManagement softwareModuleManagement) {
@@ -465,10 +486,26 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
final DistributionSetTypeRepository distributionSetTypeRepository,
final SoftwareModuleTypeRepository softwareModuleTypeRepository,
final DistributionSetRepository distributionSetRepository,
final TargetTypeRepository targetTypeRepository,
final VirtualPropertyReplacer virtualPropertyReplacer, final JpaProperties properties,
final QuotaManagement quotaManagement) {
return new JpaDistributionSetTypeManagement(distributionSetTypeRepository, softwareModuleTypeRepository,
distributionSetRepository, virtualPropertyReplacer, properties.getDatabase(), quotaManagement);
distributionSetRepository, targetTypeRepository, virtualPropertyReplacer, properties.getDatabase(), quotaManagement);
}
/**
* {@link JpaTargetTypeManagement} bean.
*
* @return a new {@link TargetTypeManagement}
*/
@Bean
@ConditionalOnMissingBean
TargetTypeManagement targetTypeManagement(final TargetTypeRepository targetTypeRepository,
final TargetRepository targetRepository, final DistributionSetTypeRepository distributionSetTypeRepository,
final VirtualPropertyReplacer virtualPropertyReplacer, final JpaProperties properties,
final QuotaManagement quotaManagement) {
return new JpaTargetTypeManagement(targetTypeRepository, targetRepository, distributionSetTypeRepository,
virtualPropertyReplacer, properties.getDatabase(), quotaManagement);
}
/**
@@ -504,11 +541,11 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
final TargetRepository targetRepository, final TargetMetadataRepository targetMetadataRepository,
final RolloutGroupRepository rolloutGroupRepository,
final DistributionSetRepository distributionSetRepository,
final TargetFilterQueryRepository targetFilterQueryRepository,
final TargetFilterQueryRepository targetFilterQueryRepository, final TargetTypeRepository targetTypeRepository,
final TargetTagRepository targetTagRepository, final EventPublisherHolder eventPublisherHolder,
final TenantAware tenantAware, final AfterTransactionCommitExecutor afterCommit,
final VirtualPropertyReplacer virtualPropertyReplacer, final JpaProperties properties) {
return new JpaTargetManagement(entityManager, quotaManagement, targetRepository, targetMetadataRepository,
return new JpaTargetManagement(entityManager, quotaManagement, targetRepository, targetTypeRepository, targetMetadataRepository,
rolloutGroupRepository, distributionSetRepository, targetFilterQueryRepository, targetTagRepository,
eventPublisherHolder, tenantAware, afterCommit, virtualPropertyReplacer, properties.getDatabase());
}

View File

@@ -20,6 +20,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
import org.springframework.data.domain.Page;
@@ -200,6 +201,15 @@ public interface TargetRepository extends BaseEntityRepository<JpaTarget, Long>,
return this.count(Specification.where(null));
}
/**
* Counts {@link Target} instances of given type in the repository.
*
* @param targetTypeId
* to search for
* @return number of found {@link Target}s
*/
long countByTargetTypeId(Long targetTypeId);
/**
* Deletes all {@link TenantAwareBaseEntity} of a given tenant. For safety
* reasons (this is a "delete everything" query after all) we add the tenant

View File

@@ -0,0 +1,186 @@
/**
* Copyright (c) 2021 Bosch.IO GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.jpa;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetTypeSpecification;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.lang.NonNull;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
/**
* {@link PagingAndSortingRepository} for {@link JpaTargetType}.
*
*/
@Transactional(readOnly = true)
public interface TargetTypeRepository
extends BaseEntityRepository<JpaTargetType, Long>, JpaSpecificationExecutor<JpaTargetType> {
/**
* Finds {@link TargetType} in the repository matching an id.
*
* Calls version based on spec to allow injecting further specs
*
* @param id id to filter for
* @return {@link Optional} of {@link TargetType}
*/
@Override
@NonNull
default Optional<JpaTargetType> findById(@NonNull Long id) {
return this.findOne(Specification.where(TargetTypeSpecification.hasId(id)));
}
/**
* @param ids
* List of ID
* @return Target type list
*/
@Override
@NonNull
default List<JpaTargetType> findAllById(Iterable<Long> ids) {
final List<Long> collectedIds = StreamSupport.stream(ids.spliterator(), true).collect(Collectors.toList());
return this.findAll(Specification.where(TargetTypeSpecification.hasIdIn(collectedIds)));
}
/**
* Deletes the {@link TargetType}s with the given target IDs.
*
* @param targetTypeIDs
* to be deleted
*/
@Modifying
@Transactional
@Query("DELETE FROM JpaTargetType t WHERE t.id IN ?1")
void deleteByIdIn(Collection<Long> targetTypeIDs);
/**
* Finds all {@link TargetType}s in the repository.
*
* Calls version with (empty) spec to allow injecting further specs
*
* @return {@link List} of {@link TargetType}s
*/
@Override
@NonNull
default List<JpaTargetType> findAll() {
return this.findAll(Specification.where(null));
}
/**
* Finds all {@link TargetType}s in the repository sorted.
*
* Calls version with (empty) spec to allow injecting further specs
*
* @param sort instructions to sort result by
* @return {@link List} of {@link TargetType}s
*/
@Override
@NonNull
default Iterable<JpaTargetType> findAll(@NonNull Sort sort) {
return this.findAll(Specification.where(null), sort);
}
/**
* Finds a page of {@link TargetType}s in the repository.
*
* Calls version with (empty) spec to allow injecting further specs
*
* @param pageable paging context
* @return {@link List} of {@link TargetType}s
*/
@Override
@NonNull
default Page<JpaTargetType> findAll(@NonNull Pageable pageable) {
return this.findAll(Specification.where(null), pageable);
}
/**
* Checks whether {@link TargetType} in the repository matching an id exists or not.
*
* Calls version based on spec to allow injecting further specs
*
* @param id id to check for
* @return true if TargetType with id exists
*/
@Override
default boolean existsById(@NonNull Long id) {
return this.exists(TargetTypeSpecification.hasId(id));
}
/**
* Checks whether {@link TargetType} in the repository matching a spec exists or not.
*
* @param spec to check for existence
* @return true if target with id exists
*/
default boolean exists(@NonNull Specification<JpaTargetType> spec) {
return this.count(spec) > 0;
}
/**
* Count number of {@link TargetType}s in the repository.
*
* Calls version with an empty spec to allow injecting further specs
*
* @return number of targetTypes in the repository
*/
@Override
default long count() {
return this.count(Specification.where(null));
}
/**
* @param tenant
* Tenant
*/
@Modifying
@Transactional
@Query("DELETE FROM JpaTargetType t WHERE t.tenant = :tenant")
void deleteByTenant(@Param("tenant") String tenant);
@Query(value = "SELECT COUNT (t.id) FROM JpaDistributionSetType t JOIN t.compatibleToTargetTypes tt WHERE tt.id = :id")
long countDsSetTypesById(@Param("id") Long id);
/**
*
* @param dsTypeId
* to search for
* @return all {@link TargetType}s in the repository with given
* {@link TargetType#getName()}
*/
default List<JpaTargetType> findByDsType(@Param("id") Long dsTypeId) {
return this.findAll(Specification.where(TargetTypeSpecification.hasDsSetType(dsTypeId)));
}
/**
*
* @param name
* to search for
* @return all {@link TargetType}s in the repository with given
* {@link TargetType#getName()}
*/
default Optional<JpaTargetType> findByName(String name){
return this.findOne(Specification.where(TargetTypeSpecification.hasName(name)));
};
}

View File

@@ -8,15 +8,26 @@
*/
package org.eclipse.hawkbit.repository.jpa.builder;
import org.eclipse.hawkbit.repository.TargetTypeManagement;
import org.eclipse.hawkbit.repository.builder.TargetBuilder;
import org.eclipse.hawkbit.repository.builder.TargetCreate;
import org.eclipse.hawkbit.repository.builder.TargetUpdate;
import org.eclipse.hawkbit.repository.model.Target;
/**
* Builder implementation for {@link Target}.
*
*/
public class JpaTargetBuilder implements TargetBuilder {
final private TargetTypeManagement targetTypeManagement;
/**
* @param targetTypeManagement
* Target type management
*/
public JpaTargetBuilder(TargetTypeManagement targetTypeManagement) {
this.targetTypeManagement = targetTypeManagement;
}
@Override
public TargetUpdate update(final String controllerId) {
@@ -25,7 +36,7 @@ public class JpaTargetBuilder implements TargetBuilder {
@Override
public TargetCreate create() {
return new JpaTargetCreate();
return new JpaTargetCreate(targetTypeManagement);
}
}

View File

@@ -8,9 +8,12 @@
*/
package org.eclipse.hawkbit.repository.jpa.builder;
import org.eclipse.hawkbit.repository.TargetTypeManagement;
import org.eclipse.hawkbit.repository.builder.AbstractTargetUpdateCreate;
import org.eclipse.hawkbit.repository.builder.TargetCreate;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.springframework.util.StringUtils;
@@ -20,8 +23,17 @@ import org.springframework.util.StringUtils;
*/
public class JpaTargetCreate extends AbstractTargetUpdateCreate<TargetCreate> implements TargetCreate {
JpaTargetCreate() {
private final TargetTypeManagement targetTypeManagement;
/**
* Constructor
*
* @param targetTypeManagement
* Target type management
*/
JpaTargetCreate(final TargetTypeManagement targetTypeManagement) {
super(null);
this.targetTypeManagement = targetTypeManagement;
}
@Override
@@ -38,6 +50,12 @@ public class JpaTargetCreate extends AbstractTargetUpdateCreate<TargetCreate> im
target.setName(name);
}
if (targetTypeId != null){
TargetType targetType = targetTypeManagement.get(targetTypeId)
.orElseThrow(() -> new EntityNotFoundException(TargetType.class, targetTypeId));
target.setTargetType(targetType);
}
target.setDescription(description);
target.setAddress(address);
target.setUpdateStatus(getStatus().orElse(TargetUpdateStatus.UNKNOWN));

View File

@@ -0,0 +1,45 @@
/**
* Copyright (c) 2021 Bosch.IO GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.jpa.builder;
import org.eclipse.hawkbit.repository.DistributionSetTypeManagement;
import org.eclipse.hawkbit.repository.TargetTypeManagement;
import org.eclipse.hawkbit.repository.builder.GenericTargetTypeUpdate;
import org.eclipse.hawkbit.repository.builder.TargetTypeBuilder;
import org.eclipse.hawkbit.repository.builder.TargetTypeCreate;
import org.eclipse.hawkbit.repository.builder.TargetTypeUpdate;
import org.eclipse.hawkbit.repository.model.TargetType;
/**
* Builder implementation for {@link TargetType}.
*
*/
public class JpaTargetTypeBuilder implements TargetTypeBuilder {
private final DistributionSetTypeManagement distributionSetTypeManagement;
/**
* Constructor
*
* @param distributionSetTypeManagement
* Distribution set type management
*/
public JpaTargetTypeBuilder(DistributionSetTypeManagement distributionSetTypeManagement) {
this.distributionSetTypeManagement = distributionSetTypeManagement;
}
@Override
public TargetTypeUpdate update(long id) {
return new GenericTargetTypeUpdate(id);
}
@Override
public TargetTypeCreate create() {
return new JpaTargetTypeCreate(distributionSetTypeManagement);
}
}

View File

@@ -0,0 +1,65 @@
/**
* Copyright (c) 2021 Bosch.IO GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.jpa.builder;
import org.eclipse.hawkbit.repository.DistributionSetTypeManagement;
import org.eclipse.hawkbit.repository.builder.AbstractTargetTypeUpdateCreate;
import org.eclipse.hawkbit.repository.builder.TargetTypeCreate;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.springframework.util.CollectionUtils;
import java.util.Collection;
import java.util.Collections;
/**
* Create/build implementation.
*
*/
public class JpaTargetTypeCreate extends AbstractTargetTypeUpdateCreate<TargetTypeCreate>
implements TargetTypeCreate {
private final DistributionSetTypeManagement distributionSetTypeManagement;
/**
* Constructor
*
* @param distributionSetTypeManagement
* Distribution set type management
*/
JpaTargetTypeCreate(final DistributionSetTypeManagement distributionSetTypeManagement) {
this.distributionSetTypeManagement = distributionSetTypeManagement;
}
@Override
public JpaTargetType build() {
final JpaTargetType result = new JpaTargetType(name, description, colour);
findDistributionSetTypeWithExceptionIfNotFound(compatible).forEach(result::addCompatibleDistributionSetType);
return result;
}
private Collection<DistributionSetType> findDistributionSetTypeWithExceptionIfNotFound(
final Collection<Long> distributionSetTypeId) {
if (CollectionUtils.isEmpty(distributionSetTypeId)) {
return Collections.emptyList();
}
final Collection<DistributionSetType> type = distributionSetTypeManagement.get(distributionSetTypeId);
if (type.size() < distributionSetTypeId.size()) {
throw new EntityNotFoundException(SoftwareModuleType.class, distributionSetTypeId);
}
return type;
}
}

View File

@@ -8,24 +8,6 @@
*/
package org.eclipse.hawkbit.repository.jpa.model;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Index;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.eclipse.hawkbit.repository.event.remote.DistributionSetTypeDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetTypeCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetTypeUpdatedEvent;
@@ -33,11 +15,30 @@ import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
import org.eclipse.persistence.annotations.CascadeOnDelete;
import org.eclipse.persistence.descriptors.DescriptorEvent;
import org.springframework.util.CollectionUtils;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Index;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
/**
* A distribution set type defines which software module types can or have to be
* {@link DistributionSet}.
@@ -72,6 +73,10 @@ public class JpaDistributionSetType extends AbstractJpaNamedEntity implements Di
@Column(name = "deleted")
private boolean deleted;
@CascadeOnDelete
@ManyToMany(mappedBy = "distributionSetTypes", targetEntity = JpaTargetType.class, fetch = FetchType.LAZY)
private List<TargetType> compatibleToTargetTypes;
public JpaDistributionSetType() {
// default public constructor for JPA
}

View File

@@ -55,6 +55,7 @@ import org.eclipse.hawkbit.repository.model.PollStatus;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetMetadata;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
import org.eclipse.hawkbit.repository.model.helper.TenantConfigurationManagementHolder;
@@ -162,6 +163,10 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
@JoinColumn(name = "target_id", nullable = false, updatable = false) }, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_attrib_target"))
private Map<String, String> controllerAttributes;
@ManyToOne(fetch = FetchType.LAZY, optional = true, targetEntity = JpaTargetType.class)
@JoinColumn(name = "target_type", nullable = true, updatable = true, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_target_relation_target_type"))
private TargetType targetType;
// set default request controller attributes to true, because we want to
// request them the first
// time
@@ -201,10 +206,16 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
: controllerId;
}
JpaTarget() {
/**
* Constructor
*/
public JpaTarget() {
// empty constructor for JPA.
}
/**
* @return assigned distribution set
*/
public DistributionSet getAssignedDistributionSet() {
return assignedDistributionSet;
}
@@ -214,6 +225,9 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
return controllerId;
}
/**
* @return tags
*/
public Set<TargetTag> getTags() {
if (tags == null) {
return Collections.emptySet();
@@ -222,6 +236,9 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
return Collections.unmodifiableSet(tags);
}
/**
* @return rollouts target group
*/
public List<RolloutTargetGroup> getRolloutTargetGroup() {
if (rolloutTargetGroup == null) {
return Collections.emptyList();
@@ -230,6 +247,11 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
return Collections.unmodifiableList(rolloutTargetGroup);
}
/**
* @param tag
* tag
* @return boolean true or false
*/
public boolean addTag(final TargetTag tag) {
if (tags == null) {
tags = new HashSet<>();
@@ -238,6 +260,11 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
return tags.add(tag);
}
/**
* @param tag
* tag
* @return boolean true or false
*/
public boolean removeTag(final TargetTag tag) {
if (tags == null) {
return false;
@@ -246,14 +273,25 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
return tags.remove(tag);
}
/**
* @param assignedDistributionSet
* Distribution set
*/
public void setAssignedDistributionSet(final DistributionSet assignedDistributionSet) {
this.assignedDistributionSet = (JpaDistributionSet) assignedDistributionSet;
}
/**
* @param controllerId
* Controller ID
*/
public void setControllerId(final String controllerId) {
this.controllerId = controllerId;
}
/**
* @return list of action
*/
public List<Action> getActions() {
if (actions == null) {
return Collections.emptyList();
@@ -262,6 +300,11 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
return Collections.unmodifiableList(actions);
}
/**
* @param action
* Action
* @return boolean true or false
*/
public boolean addAction(final Action action) {
if (actions == null) {
actions = new ArrayList<>();
@@ -285,6 +328,10 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
return null;
}
/**
* @param securityToken
* token value
*/
public void setSecurityToken(final String securityToken) {
this.securityToken = securityToken;
}
@@ -348,10 +395,29 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
return updateStatus;
}
@Override
public TargetType getTargetType() {
return targetType;
}
/**
* @param type
* Target type
*/
public void setTargetType(final TargetType type) {
this.targetType = type;
}
/**
* @return Distribution set
*/
public JpaDistributionSet getInstalledDistributionSet() {
return installedDistributionSet;
}
/**
* @return controller attributes
*/
public Map<String, String> getControllerAttributes() {
return controllerAttributes;
}
@@ -361,6 +427,9 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
return requestControllerAttributes;
}
/**
* @return target metadata
*/
public List<TargetMetadata> getMetadata() {
if (metadata == null) {
return Collections.emptyList();
@@ -375,26 +444,50 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
+ "]";
}
/**
* @param address
* Address
*/
public void setAddress(final String address) {
this.address = address;
}
/**
* @param lastTargetQuery
* last query ID
*/
public void setLastTargetQuery(final Long lastTargetQuery) {
this.lastTargetQuery = lastTargetQuery;
}
/**
* @param installationDate
* installation date
*/
public void setInstallationDate(final Long installationDate) {
this.installationDate = installationDate;
}
/**
* @param installedDistributionSet
* Distribution set
*/
public void setInstalledDistributionSet(final JpaDistributionSet installedDistributionSet) {
this.installedDistributionSet = installedDistributionSet;
}
/**
* @param updateStatus
* Status
*/
public void setUpdateStatus(final TargetUpdateStatus updateStatus) {
this.updateStatus = updateStatus;
}
/**
* @param requestControllerAttributes
* Attributes
*/
public void setRequestControllerAttributes(final boolean requestControllerAttributes) {
this.requestControllerAttributes = requestControllerAttributes;
}

View File

@@ -0,0 +1,156 @@
/**
* Copyright (c) 2021 Bosch.IO GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.jpa.model;
import org.eclipse.hawkbit.repository.event.remote.TargetTypeDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetTypeCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetTypeUpdatedEvent;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
import org.eclipse.persistence.annotations.CascadeOnDelete;
import org.eclipse.persistence.descriptors.DescriptorEvent;
import javax.persistence.Column;
import javax.persistence.ConstraintMode;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.ForeignKey;
import javax.persistence.Index;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import javax.validation.constraints.Size;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
* A target type defines which distribution set types can or have to be
* {@link Target}.
*
*/
@Entity
@Table(name = "sp_target_type", indexes = {
@Index(name = "sp_idx_target_type_prim", columnList = "tenant,id") }, uniqueConstraints = {
@UniqueConstraint(columnNames = { "name", "tenant" }, name = "uk_target_type_name")})
public class JpaTargetType extends AbstractJpaNamedEntity implements TargetType, EventAwareEntity{
private static final long serialVersionUID = 1L;
@Column(name = "colour", nullable = true, length = TargetType.COLOUR_MAX_SIZE)
@Size(max = TargetType.COLOUR_MAX_SIZE)
private String colour;
@CascadeOnDelete
@ManyToMany(targetEntity = JpaDistributionSetType.class)
@JoinTable(name = "sp_target_type_ds_type_relation", joinColumns = {
@JoinColumn(name = "target_type", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_target_type_relation_target_type"))}, inverseJoinColumns = {
@JoinColumn(name = "distribution_set_type", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_target_type_relation_ds_type"))})
private Set<DistributionSetType> distributionSetTypes;
@OneToMany(targetEntity = JpaTarget.class, mappedBy = "targetType", fetch = FetchType.LAZY)
private Set<Target> targets;
/**
* Constructor
*/
public JpaTargetType() {
// default public constructor for JPA
}
/**
* Constructor
*
* @param name
* Type name
* @param description
* Description
* @param colour
* Colour
*/
public JpaTargetType(String name, String description, String colour) {
super(name,description);
this.colour = colour;
}
/**
* @param dsSetType
* Distribution set type
* @return Target type
*/
public JpaTargetType addCompatibleDistributionSetType(final DistributionSetType dsSetType) {
if (distributionSetTypes == null) {
distributionSetTypes = new HashSet<>();
}
distributionSetTypes.add(dsSetType);
return this;
}
/**
* @param dsTypeId
* Distribution set type ID
* @return Target type
*/
public JpaTargetType removeDistributionSetType(final Long dsTypeId) {
if (distributionSetTypes == null) {
return this;
}
distributionSetTypes.stream().filter(element -> element.getId().equals(dsTypeId)).findAny()
.ifPresent(distributionSetTypes::remove);
return this;
}
@Override
public Set<DistributionSetType> getCompatibleDistributionSetTypes() {
if (distributionSetTypes == null) {
return Collections.emptySet();
}
return Collections.unmodifiableSet(distributionSetTypes);
}
@Override
public Set<Target> getTargets() {
return targets;
}
@Override
public String getColour() {
return colour;
}
public void setColour(final String colour) {
this.colour = colour;
}
@Override
public void fireCreateEvent(DescriptorEvent descriptorEvent) {
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(
new TargetTypeCreatedEvent(this, EventPublisherHolder.getInstance().getApplicationId()));
}
@Override
public void fireUpdateEvent(DescriptorEvent descriptorEvent) {
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(
new TargetTypeUpdatedEvent(this, EventPublisherHolder.getInstance().getApplicationId()));
}
@Override
public void fireDeleteEvent(DescriptorEvent descriptorEvent) {
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new TargetTypeDeletedEvent(
getTenant(), getId(), getClass().getName(), EventPublisherHolder.getInstance().getApplicationId()));
}
}

View File

@@ -0,0 +1,149 @@
/**
* Copyright (c) 2021 Bosch.IO GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.jpa.specifications;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType_;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType_;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.springframework.data.jpa.domain.Specification;
import javax.persistence.criteria.SetJoin;
import java.util.Collection;
/**
* Specifications class for {@link TargetType}s. The class provides Spring Data JPQL
* Specifications.
*
*/
public final class TargetTypeSpecification {
private TargetTypeSpecification() {
// utility class
}
/**
* {@link Specification} for retrieving {@link TargetType}s by controllerId
*
* @param id
* to search for
*
* @return the {@link TargetType} {@link Specification}
*/
public static Specification<JpaTargetType> hasId(final Long id) {
return (targetRoot, query, cb) -> cb.equal(targetRoot.get(JpaTargetType_.id), id);
}
/**
* {@link Specification} for retrieving {@link TargetType}s by controllerId
*
* @param id
* to search for
*
* @return the {@link TargetType} {@link Specification}
*/
public static Specification<JpaTargetType> hasTarget(final long id) {
return (targetRoot, query, cb) -> {
final SetJoin<JpaTargetType, JpaTarget> join = targetRoot.join(JpaTargetType_.targets);
return cb.equal(join.get(JpaTarget_.id), id);
};
}
/**
* {@link Specification} for retrieving {@link TargetType}s by controllerId
*
* @param ids
* to search for
*
* @return the {@link TargetType} {@link Specification}
*/
public static Specification<JpaTargetType> hasTarget(final Collection<Long> ids) {
return (targetRoot, query, cb) -> {
final SetJoin<JpaTargetType, JpaTarget> join = targetRoot.join(JpaTargetType_.targets);
return join.get(JpaTarget_.id).in(ids);
};
}
/**
* {@link Specification} for retrieving {@link TargetType}s by controllerId
*
* @param controllerId
* to search for
*
* @return the {@link TargetType} {@link Specification}
*/
public static Specification<JpaTargetType> hasTargetControllerId(final String controllerId) {
return (targetRoot, query, cb) -> {
final SetJoin<JpaTargetType, JpaTarget> join = targetRoot.join(JpaTargetType_.targets);
return cb.equal(join.get(JpaTarget_.controllerId), controllerId);
};
}
/**
* {@link Specification} for retrieving {@link TargetType}s by controllerId
*
* @param controllerIds
* to search for
*
* @return the {@link TargetType} {@link Specification}
*/
public static Specification<JpaTargetType> hasTargetControllerIdIn(final Collection<String> controllerIds) {
return (targetRoot, query, cb) -> {
final SetJoin<JpaTargetType, JpaTarget> join = targetRoot.join(JpaTargetType_.targets);
return join.get(JpaTarget_.controllerId).in(controllerIds);
};
}
/**
* {@link Specification} for retrieving {@link TargetType}s by controllerId
*
* @param ids
* to search for
*
* @return the {@link TargetType} {@link Specification}
*/
public static Specification<JpaTargetType> hasIdIn(final Collection<Long> ids) {
return (targetRoot, query, cb) -> targetRoot.get(JpaTargetType_.id).in(ids);
}
/**
* {@link Specification} for retrieving {@link TargetType}s based on a
* {@link DistributionSetType} name.
*
* @param dsTypeId
* to search for
*
* @return the {@link TargetType} {@link Specification}
*/
public static Specification<JpaTargetType> hasDsSetType(final Long dsTypeId) {
return (targetRoot, query, cb) -> {
final SetJoin<JpaTargetType, JpaDistributionSetType> join = targetRoot.join(JpaTargetType_.distributionSetTypes);
return cb.equal(join.get(JpaDistributionSetType_.id), dsTypeId);
};
}
/**
* {@link Specification} for retrieving {@link TargetType} with
* given {@link TargetType#getName()} including fetching the
* elements list.
*
* @param name
* to search
* @return the {@link TargetType} {@link Specification}
*/
public static Specification<JpaTargetType> hasName(final String name) {
return (targetRoot, query, cb) -> cb.equal(targetRoot.get(JpaTargetType_.name), name);
}
}

View File

@@ -0,0 +1,32 @@
CREATE TABLE sp_target_type
(
id BIGINT GENERATED always AS IDENTITY NOT NULL,
tenant VARCHAR(40) NOT NULL,
colour VARCHAR(16),
created_at BIGINT NOT NULL,
created_by VARCHAR(64) NOT NULL,
description VARCHAR(512),
last_modified_at BIGINT NOT NULL,
last_modified_by VARCHAR(64) NOT NULL,
name VARCHAR(64) NOT NULL,
optlock_revision INTEGER,
PRIMARY KEY (id)
);
CREATE INDEX sp_idx_target_type_prim
ON sp_target_type (tenant, id);
CREATE TABLE sp_target_type_ds_type_relation
(
target_type BIGINT NOT NULL,
distribution_set_type BIGINT NOT NULL,
PRIMARY KEY (target_type, distribution_set_type)
);
ALTER TABLE sp_target_type ADD CONSTRAINT uk_target_type_name UNIQUE (name, tenant);
ALTER TABLE sp_target ADD COLUMN target_type BIGINT;
ALTER TABLE sp_target ADD CONSTRAINT fk_target_relation_target_type FOREIGN KEY (target_type) REFERENCES sp_target_type (id) ON DELETE SET NULL;
ALTER TABLE sp_target_type_ds_type_relation ADD CONSTRAINT fk_target_type_relation_target_type FOREIGN KEY (target_type) REFERENCES sp_target_type (id) ON DELETE CASCADE;
ALTER TABLE sp_target_type_ds_type_relation ADD CONSTRAINT fk_target_type_relation_ds_type FOREIGN KEY (distribution_set_type) REFERENCES sp_distribution_set_type (id) ON DELETE CASCADE;

View File

@@ -0,0 +1,50 @@
create table sp_target_type
(
id bigint generated by default as identity,
created_at bigint,
created_by varchar(64),
last_modified_at bigint,
last_modified_by varchar(64),
optlock_revision bigint,
tenant varchar(40) not null,
description varchar(512),
name varchar(64) not null,
colour varchar(16),
primary key (id)
);
create table sp_target_type_ds_type_relation
(
target_type bigint not null,
distribution_set_type bigint not null,
primary key (target_type, distribution_set_type)
);
alter table sp_target_type
add constraint uk_target_type_name unique (name, tenant);
create index sp_idx_target_type_prim on sp_target_type (tenant, id);
alter table sp_target
add column target_type bigint;
alter table sp_target
add constraint fk_target_relation_target_type
foreign key (target_type)
references sp_target_type
on delete set null;
alter table sp_target_type_ds_type_relation
add constraint fk_target_type_relation_target_type
foreign key (target_type)
references sp_target_type
on delete cascade;
alter table sp_target_type_ds_type_relation
add constraint fk_target_type_relation_ds_type
foreign key (distribution_set_type)
references sp_distribution_set_type
on delete cascade;

View File

@@ -0,0 +1,47 @@
create table sp_target_type
(
id bigint not null auto_increment,
created_at bigint,
created_by varchar(64),
last_modified_at bigint,
last_modified_by varchar(64),
optlock_revision bigint,
tenant varchar(40) not null,
description varchar(512),
name varchar(64) not null,
colour varchar(16),
primary key (id)
);
create table sp_target_type_ds_type_relation
(
target_type bigint not null,
distribution_set_type bigint not null,
primary key (target_type, distribution_set_type)
);
alter table sp_target_type
add constraint uk_target_type_name unique (name, tenant);
create index sp_idx_target_type_prim on sp_target_type (tenant, id);
alter table sp_target
add column target_type bigint;
alter table sp_target
add constraint fk_target_relation_target_type
foreign key (target_type)
references sp_target_type (id)
on delete set null;
alter table sp_target_type_ds_type_relation
add constraint fk_target_type_relation_target_type
foreign key (target_type)
references sp_target_type (id)
on delete cascade;
alter table sp_target_type_ds_type_relation
add constraint fk_target_type_relation_ds_type
foreign key (distribution_set_type)
references sp_distribution_set_type (id)
on delete cascade;

View File

@@ -0,0 +1,65 @@
-- ------------ Write CREATE-SEQUENCE-stage scripts -----------
CREATE SEQUENCE IF NOT EXISTS sp_target_type_seq
INCREMENT BY 1
START WITH 1
NO CYCLE;
-- ------------ Write CREATE-TABLE-stage scripts -----------
CREATE TABLE sp_target_type(
id BIGINT NOT NULL DEFAULT nextval('sp_target_type_seq'),
created_at BIGINT,
created_by VARCHAR(64),
last_modified_at BIGINT,
last_modified_by VARCHAR(64),
optlock_revision BIGINT,
tenant VARCHAR(40) NOT NULL,
description VARCHAR(512),
name VARCHAR(64),
colour VARCHAR(16)
)
WITH (
OIDS=FALSE
);
CREATE TABLE sp_target_type_ds_type_relation(
target_type BIGINT NOT NULL,
distribution_set_type BIGINT NOT NULL
)
WITH (
OIDS=FALSE
);
-- ------------ Alter Table and Write INDEX scripts -----------
ALTER TABLE sp_target_type
ADD CONSTRAINT pk_sp_target_type PRIMARY KEY (id);
ALTER TABLE sp_target_type
ADD CONSTRAINT uk_target_type_name UNIQUE (name, tenant);
CREATE INDEX sp_idx_target_type_prim
ON sp_target_type
USING BTREE (tenant, id);
ALTER TABLE sp_target
ADD COLUMN target_type BIGINT;
ALTER TABLE sp_target
ADD CONSTRAINT fk_target_relation_target_type FOREIGN KEY (target_type)
REFERENCES sp_target_type (id)
ON UPDATE RESTRICT
ON DELETE SET NULL;
ALTER TABLE sp_target_type_ds_type_relation
ADD CONSTRAINT fk_target_type_relation_target_type FOREIGN KEY (target_type)
REFERENCES sp_target_type (id)
ON UPDATE RESTRICT
ON DELETE CASCADE;
ALTER TABLE sp_target_type_ds_type_relation
ADD CONSTRAINT fk_target_type_relation_ds_type FOREIGN KEY (distribution_set_type)
REFERENCES sp_distribution_set_type (id)
ON UPDATE RESTRICT
ON DELETE CASCADE;

View File

@@ -0,0 +1,27 @@
CREATE TABLE sp_target_type
(
id NUMERIC(19) IDENTITY NOT NULL,
tenant VARCHAR(40) NOT NULL,
colour VARCHAR(16) NULL,
created_at NUMERIC(19) NOT NULL,
created_by VARCHAR(64) NOT NULL,
description VARCHAR(512) NULL,
last_modified_at NUMERIC(19) NOT NULL,
last_modified_by VARCHAR(64) NOT NULL,
name VARCHAR(64) NOT NULL,
optlock_revision INTEGER NULL,
PRIMARY KEY (id)
);
CREATE INDEX sp_idx_target_type_prim ON sp_target_type (tenant, id);
CREATE TABLE sp_target_type_ds_type_relation
(
target_type NUMERIC(19) NOT NULL,
distribution_set_type NUMERIC(19) NOT NULL,
PRIMARY KEY (target_type, distribution_set_type)
);
ALTER TABLE sp_target_type ADD CONSTRAINT uk_target_type_name UNIQUE (name, tenant);
ALTER TABLE sp_target ADD target_type NUMERIC(19) NULL;
ALTER TABLE sp_target ADD CONSTRAINT fk_target_relation_target_type FOREIGN KEY (target_type) REFERENCES sp_target_type (id) ON DELETE SET NULL;
ALTER TABLE sp_target_type_ds_type_relation ADD CONSTRAINT fk_target_type_relation_target_type FOREIGN KEY (target_type) REFERENCES sp_target_type (id) ON DELETE CASCADE;
ALTER TABLE sp_target_type_ds_type_relation ADD CONSTRAINT fk_target_type_relation_ds_type FOREIGN KEY (distribution_set_type) REFERENCES sp_distribution_set_type (id) ON DELETE CASCADE;

View File

@@ -76,6 +76,9 @@ public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest
@Autowired
protected TargetTagRepository targetTagRepository;
@Autowired
protected TargetTypeRepository targetTypeRepository;
@Autowired
protected DistributionSetTagRepository distributionSetTagRepository;

View File

@@ -20,6 +20,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import javax.validation.ConstraintViolationException;
@@ -27,6 +28,7 @@ import javax.validation.ConstraintViolationException;
import org.apache.commons.lang3.RandomStringUtils;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.repository.FilterParams;
import org.eclipse.hawkbit.repository.builder.TargetUpdate;
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetAttributesRequestedEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent;
@@ -53,6 +55,7 @@ import org.eclipse.hawkbit.repository.model.Tag;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetMetadata;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.test.matcher.Expect;
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
import org.eclipse.hawkbit.repository.test.util.WithSpringAuthorityRule;
@@ -1038,6 +1041,67 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest {
assertThat(updated.getTarget().getId()).isEqualTo(target.getId());
}
@Test
@WithUser(allSpPermissions = true)
@Description("Checks that target type for a target can be created, updated and unassigned.")
public void createAndUpdateTargetTypeInTarget() {
// create a target type
List<TargetType> targetTypes = testdataFactory.createTargetTypes("targettype", 2);
assertThat(targetTypes).hasSize(2);
// create a target
final Target target = testdataFactory.createTarget("target1", "testtarget", targetTypes.get(0).getId());
// initial opt lock revision must be one
Optional<JpaTarget> targetFound = targetRepository.findById(target.getId());
assertThat(targetFound).isPresent();
assertThat(targetFound.get().getOptLockRevision()).isEqualTo(1);
assertThat(targetFound.get().getTargetType().getId()).isEqualTo(targetTypes.get(0).getId());
// update the target type
TargetUpdate targetUpdate = entityFactory.target().update(target.getControllerId()).targetType(targetTypes.get(1).getId());
targetManagement.update(targetUpdate);
// opt lock revision must be changed
Optional<JpaTarget> targetFound1 = targetRepository.findById(target.getId());
assertThat(targetFound1).isPresent();
assertThat(targetFound1.get().getOptLockRevision()).isEqualTo(2);
assertThat(targetFound1.get().getTargetType().getId()).isEqualTo(targetTypes.get(1).getId());
// unassign the target type
targetManagement.unAssignType(target.getControllerId());
// opt lock revision must be changed
Optional<JpaTarget> targetFound2 = targetRepository.findById(target.getId());
assertThat(targetFound2).isPresent();
assertThat(targetFound2.get().getOptLockRevision()).isEqualTo(3);
assertThat(targetFound2.get().getTargetType()).isNull();
}
@Test
@WithUser(allSpPermissions = true)
@Description("Checks that target type to a target can be assigned.")
public void assignTargetTypeInTarget() {
// create a target
final Target target = testdataFactory.createTarget("target1", "testtarget");
// initial opt lock revision must be one
Optional<JpaTarget> targetFound = targetRepository.findById(target.getId());
assertThat(targetFound).isPresent();
assertThat(targetFound.get().getOptLockRevision()).isEqualTo(1);
assertThat(targetFound.get().getTargetType()).isNull();
// create a target type
TargetType targetType = testdataFactory.findOrCreateTargetType("targettype");
assertThat(targetType).isNotNull();
// assign target type to target
targetManagement.assignType(targetFound.get().getControllerId(), targetType.getId());
// opt lock revision must be changed
Optional<JpaTarget> targetFound1 = targetRepository.findById(target.getId());
assertThat(targetFound1).isPresent();
assertThat(targetFound1.get().getOptLockRevision()).isEqualTo(2);
assertThat(targetFound1.get().getTargetType().getId()).isEqualTo(targetType.getId());
}
@Test
@Description("Queries and loads the metadata related to a given target.")
public void findAllTargetMetadataByControllerId() {

View File

@@ -0,0 +1,230 @@
/**
* Copyright (c) 2021 Bosch.IO GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.jpa;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.qameta.allure.Step;
import io.qameta.allure.Story;
import org.apache.commons.lang3.RandomStringUtils;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetTypeCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetTypeUpdatedEvent;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.NamedEntity;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.test.matcher.Expect;
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
import org.junit.jupiter.api.Test;
import javax.validation.ConstraintViolationException;
import java.util.Collections;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.junit.jupiter.api.Assertions.assertThrows;
@Feature("Component Tests - Repository")
@Story("Target Type Management")
public class TargetTypeManagementTest extends AbstractJpaIntegrationTest{
@Test
@Description("Verifies that management get access react as specified on calls for non existing entities by means "
+ "of Optional not present.")
@ExpectEvents({ @Expect(type = TargetTypeCreatedEvent.class, count = 0) })
public void nonExistingEntityAccessReturnsNotPresent() {
assertThat(targetTypeManagement.get(NOT_EXIST_IDL)).isNotPresent();
assertThat(targetTypeManagement.getByName(NOT_EXIST_ID)).isNotPresent();
}
@Test
@Description("Verifies that management queries react as specified on calls for non existing entities "
+ " by means of throwing EntityNotFoundException.")
@ExpectEvents({ @Expect(type = TargetTypeUpdatedEvent.class, count = 0) })
public void entityQueriesReferringToNotExistingEntitiesThrowsException() {
verifyThrownExceptionBy(() -> targetTypeManagement.delete(NOT_EXIST_IDL), "TargetType");
verifyThrownExceptionBy(() -> targetTypeManagement.update(entityFactory.targetType().update(NOT_EXIST_IDL)),
"TargetType");
}
@Test
@Description("Verify that a target type with invalid properties cannot be created or updated")
public void createAndUpdateTargetTypeWithInvalidFields() {
final TargetType targetType = targetTypeManagement
.create(entityFactory.targetType().create().name("targettype1").description("targettypedes1"));
createAndUpdateTargetTypeWithInvalidDescription(targetType);
createAndUpdateTargetTypeWithInvalidColour(targetType);
createAndUpdateTargetTypeWithInvalidName(targetType);
}
@Step
private void createAndUpdateTargetTypeWithInvalidDescription(final TargetType targetType) {
assertThatExceptionOfType(ConstraintViolationException.class)
.isThrownBy(() -> targetTypeManagement.create(
entityFactory.targetType().create().name("a").description(RandomStringUtils.randomAlphanumeric(TargetType.DESCRIPTION_MAX_SIZE + 1))))
.as("targetType with too long description should not be created");
assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(
() -> targetTypeManagement.create(entityFactory.targetType().create().name("a").description(INVALID_TEXT_HTML)))
.as("targetType with invalid description should not be created");
assertThatExceptionOfType(ConstraintViolationException.class)
.isThrownBy(() -> targetTypeManagement.update(
entityFactory.targetType().update(targetType.getId()).description(RandomStringUtils.randomAlphanumeric(TargetType.DESCRIPTION_MAX_SIZE + 1))))
.as("targetType with too long description should not be updated");
assertThatExceptionOfType(ConstraintViolationException.class)
.isThrownBy(() -> targetTypeManagement
.update(entityFactory.targetType().update(targetType.getId()).description(INVALID_TEXT_HTML)))
.as("targetType with invalid description should not be updated");
}
@Step
private void createAndUpdateTargetTypeWithInvalidColour(final TargetType targetType) {
assertThatExceptionOfType(ConstraintViolationException.class)
.isThrownBy(() -> targetTypeManagement.create(
entityFactory.targetType().create().name("a").colour(RandomStringUtils.randomAlphanumeric(TargetType.COLOUR_MAX_SIZE + 1))))
.as("targetType with too long colour should not be created");
assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(
() -> targetTypeManagement.create(entityFactory.targetType().create().name("a").colour(INVALID_TEXT_HTML)))
.as("targetType with invalid colour should not be created");
assertThatExceptionOfType(ConstraintViolationException.class)
.isThrownBy(() -> targetTypeManagement.update(
entityFactory.targetType().update(targetType.getId()).colour(RandomStringUtils.randomAlphanumeric(TargetType.COLOUR_MAX_SIZE + 1))))
.as("targetType with too long colour should not be updated");
assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(
() -> targetTypeManagement.update(entityFactory.targetType().update(targetType.getId()).colour(INVALID_TEXT_HTML)))
.as("targetType with invalid colour should not be updated");
}
@Step
private void createAndUpdateTargetTypeWithInvalidName(final TargetType targetType) {
assertThatExceptionOfType(ConstraintViolationException.class)
.isThrownBy(() -> targetTypeManagement
.create(entityFactory.targetType().create().name(RandomStringUtils.randomAlphanumeric(
NamedEntity.NAME_MAX_SIZE + 1))))
.as("targetType with too long name should not be created");
assertThatExceptionOfType(ConstraintViolationException.class)
.isThrownBy(() -> targetTypeManagement.create(entityFactory.targetType().create().name(INVALID_TEXT_HTML)))
.as("targetType with invalid name should not be created");
assertThatExceptionOfType(ConstraintViolationException.class)
.isThrownBy(() -> targetTypeManagement
.update(entityFactory.targetType().update(targetType.getId()).name(RandomStringUtils.randomAlphanumeric(
NamedEntity.NAME_MAX_SIZE + 1))))
.as("targetType with too long name should not be updated");
assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(
() -> targetTypeManagement.update(entityFactory.targetType().update(targetType.getId()).name(INVALID_TEXT_HTML)))
.as("targetType with invalid name should not be updated");
assertThatExceptionOfType(ConstraintViolationException.class)
.isThrownBy(() -> targetTypeManagement.update(entityFactory.targetType().update(targetType.getId()).name("")))
.as("targetType with too short name should not be updated");
}
@Test
@Description("Tests the successful assignment of compatible distribution set types to a target type")
public void assignCompatibleDistributionSetTypesToTargetType(){
final TargetType targetType = targetTypeManagement
.create(entityFactory.targetType().create().name("targettype1").description("targettypedes1"));
DistributionSetType distributionSetType = testdataFactory.findOrCreateDistributionSetType("testDst", "dst1");
targetTypeManagement.assignCompatibleDistributionSetTypes(targetType.getId(), Collections.singletonList(distributionSetType.getId()));
Optional<JpaTargetType> targetTypeWithDsTypes = targetTypeRepository.findById(targetType.getId());
assertThat(targetTypeWithDsTypes).isPresent();
assertThat(targetTypeWithDsTypes.get().getCompatibleDistributionSetTypes()).extracting("key").contains("testDst");
}
@Test
@Description("Tests the successful removal of compatible distribution set types to a target type")
public void unassignCompatibleDistributionSetTypesToTargetType(){
final TargetType targetType = targetTypeManagement
.create(entityFactory.targetType().create().name("targettype11").description("targettypedes11"));
DistributionSetType distributionSetType = testdataFactory.findOrCreateDistributionSetType("testDst1", "dst11");
targetTypeManagement.assignCompatibleDistributionSetTypes(targetType.getId(), Collections.singletonList(distributionSetType.getId()));
Optional<JpaTargetType> targetTypeWithDsTypes = targetTypeRepository.findById(targetType.getId());
assertThat(targetTypeWithDsTypes).isPresent();
assertThat(targetTypeWithDsTypes.get().getCompatibleDistributionSetTypes()).extracting("key").contains("testDst1");
targetTypeManagement.unassignDistributionSetType(targetType.getId(),distributionSetType.getId());
Optional<JpaTargetType> targetTypeWithDsTypes1 = targetTypeRepository.findById(targetType.getId());
assertThat(targetTypeWithDsTypes1).isPresent();
assertThat(targetTypeWithDsTypes1.get().getCompatibleDistributionSetTypes()).hasSize(0);
}
@Test
@Description("Ensures that all types are retrieved through repository.")
public void findAllTargetTypes() {
testdataFactory.createTargetTypes("targettype", 10);
assertThat(targetTypeRepository.findAll()).as("Target type size").hasSize(10);
}
@Test
@Description("Ensures that a created target type is persisted in the repository as defined.")
public void createTargetType() {
final TargetType targetType = targetTypeManagement
.create(entityFactory.targetType().create().name("targettype1").description("targettypedes1").colour("colour1"));
assertThat(targetTypeRepository.findByName("targettype1").get().getDescription()).as("type found")
.isEqualTo("targettypedes1");
assertThat(targetTypeManagement.getByName("targettype1").get().getColour()).as("type found").isEqualTo("colour1");
assertThat(targetTypeManagement.get(targetType.getId()).get().getColour()).as("type found").isEqualTo("colour1");
}
@Test
@Description("Ensures that a deleted target type is removed from the repository as defined.")
public void deleteTargetType() {
// create test data
final TargetType targetType = targetTypeManagement
.create(entityFactory.targetType().create().name("targettype11").description("targettypedes11"));
assertThat(targetTypeRepository.findByName("targettype11").get().getDescription()).as("type found")
.isEqualTo("targettypedes11");
targetTypeManagement.delete(targetType.getId());
assertThat(targetTypeRepository.findById(targetType.getId())).as("No target type should be found").isNotPresent();
}
@Test
@Description("Tests the name update of a target type.")
public void updateTargetType() {
final TargetType targetType = targetTypeManagement
.create(entityFactory.targetType().create().name("targettype111").description("targettypedes111"));
assertThat(targetTypeRepository.findByName("targettype111").get().getDescription()).as("type found")
.isEqualTo("targettypedes111");
targetTypeManagement.update(entityFactory.targetType().update(targetType.getId()).name("updatedtargettype111"));
assertThat(targetTypeRepository.findByName("updatedtargettype111")).as("Updated target type should be found").isPresent();
}
@Test
@Description("Ensures that a target type cannot be created if one exists already with that name (expects EntityAlreadyExistsException).")
public void failedDuplicateTargetTypeNameException() {
targetTypeManagement.create(entityFactory.targetType().create().name("targettype123"));
assertThrows(EntityAlreadyExistsException.class, () -> targetTypeManagement.create(entityFactory.targetType().create().name("targettype123")));
}
@Test
@Description("Ensures that a target type cannot be updated to a name that already exists (expects EntityAlreadyExistsException).")
public void failedDuplicateTargetTypeNameExceptionAfterUpdate() {
targetTypeManagement.create(entityFactory.targetType().create().name("targettype1234"));
TargetType targetType = targetTypeManagement.create(entityFactory.targetType().create().name("targettype12345"));
assertThrows(EntityAlreadyExistsException.class, () -> targetTypeManagement.update(entityFactory.targetType().update(targetType.getId()).name("targettype1234")));
}
}

View File

@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.repository.jpa.event;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
@@ -20,10 +21,13 @@ import org.eclipse.hawkbit.repository.event.remote.DistributionSetDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.RolloutDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.SoftwareModuleDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetTypeDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleUpdatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetTypeCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetTypeUpdatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.jpa.event.RepositoryEntityEventTest.RepositoryTestConfiguration;
@@ -31,6 +35,7 @@ import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
@@ -88,6 +93,38 @@ public class RepositoryEntityEventTest extends AbstractJpaIntegrationTest {
assertThat(targetDeletedEvent.getEntityId()).isEqualTo(createdTarget.getId());
}
@Test
@Description("Verifies that the target type created event is published when a target type has been created")
public void targetTypeCreatedEventIsPublished() throws InterruptedException {
TargetType createdTargetType = testdataFactory.findOrCreateTargetType("targettype");
TargetTypeCreatedEvent targetTypeCreatedEvent = eventListener.waitForEvent(TargetTypeCreatedEvent.class);
assertThat(targetTypeCreatedEvent).isNotNull();
assertThat(targetTypeCreatedEvent.getEntity().getId()).isEqualTo(createdTargetType.getId());
}
@Test
@Description("Verifies that the target type updated event is published when a target type has been updated")
public void targetTypeUpdatedEventIsPublished() throws InterruptedException {
TargetType createdTargetType = testdataFactory.findOrCreateTargetType("targettype");
targetTypeManagement.update(entityFactory.targetType().update(createdTargetType.getId()).name("updatedtargettype"));
TargetTypeUpdatedEvent targetTypeUpdatedEvent = eventListener.waitForEvent(TargetTypeUpdatedEvent.class);
assertThat(targetTypeUpdatedEvent).isNotNull();
assertThat(targetTypeUpdatedEvent.getEntity().getId()).isEqualTo(createdTargetType.getId());
}
@Test
@Description("Verifies that the target type deleted event is published when a target type has been deleted")
public void targetTypeDeletedEventIsPublished() throws InterruptedException {
TargetType createdTargetType = testdataFactory.findOrCreateTargetType("targettype");
targetTypeManagement.delete(createdTargetType.getId());
TargetTypeDeletedEvent targetTypeDeletedEvent = eventListener.waitForEvent(TargetTypeDeletedEvent.class);
assertThat(targetTypeDeletedEvent).isNotNull();
assertThat(targetTypeDeletedEvent.getEntityId()).isEqualTo(createdTargetType.getId());
}
@Test
@Description("Verifies that the rollout deleted event is published when a rollout has been deleted")
public void rolloutDeletedEventIsPublished() throws InterruptedException {

View File

@@ -44,6 +44,7 @@ import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.TargetTagManagement;
import org.eclipse.hawkbit.repository.TargetTypeManagement;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
@@ -144,6 +145,9 @@ public abstract class AbstractIntegrationTest {
@Autowired
protected TargetManagement targetManagement;
@Autowired
protected TargetTypeManagement targetTypeManagement;
@Autowired
protected TargetFilterQueryManagement targetFilterQueryManagement;

View File

@@ -63,7 +63,7 @@ public class JpaTestRepositoryManagement {
return null;
});
} catch (final Exception e) {
LOGGER.error("Error hile delete tenant", e);
LOGGER.error("Error while delete tenant", e);
}
});
}

View File

@@ -37,8 +37,10 @@ import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.TargetTagManagement;
import org.eclipse.hawkbit.repository.TargetTypeManagement;
import org.eclipse.hawkbit.repository.builder.TagCreate;
import org.eclipse.hawkbit.repository.builder.TargetCreate;
import org.eclipse.hawkbit.repository.builder.TargetTypeCreate;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.ActionStatus;
@@ -61,6 +63,7 @@ import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
@@ -123,6 +126,8 @@ public class TestdataFactory {
*/
public static final String SM_TYPE_APP = "application";
public static final String DEFAULT_COLOUR = "#000000";
@Autowired
private ControllerManagement controllerManagament;
@@ -141,6 +146,9 @@ public class TestdataFactory {
@Autowired
private TargetManagement targetManagement;
@Autowired
private TargetTypeManagement targetTypeManagement;
@Autowired
private DeploymentManagement deploymentManagement;
@@ -638,6 +646,22 @@ public class TestdataFactory {
return target;
}
/**
* @param controllerId
* of the target
* @param targetName
* name of the target
* @param targetTypeId
* target type id
* @return persisted {@link Target}
*/
public Target createTarget(final String controllerId, final String targetName, final Long targetTypeId) {
final Target target = targetManagement
.create(entityFactory.target().create().controllerId(controllerId).name(targetName).targetType(targetTypeId));
assertTargetProperlyCreated(target);
return target;
}
private void assertTargetProperlyCreated(final Target target) {
assertThat(target.getCreatedBy()).isNotNull();
assertThat(target.getCreatedAt()).isNotNull();
@@ -1160,4 +1184,54 @@ public class TestdataFactory {
rolloutManagement.handleRollouts();
return newRollout;
}
/**
* Finds {@link TargetType} in repository with given
* {@link TargetType#getName()} or creates if it does not exist yet.
* No ds types are assigned on creation.
*
* @param targetTypeName
* {@link TargetType#getName()}
*
* @return persisted {@link TargetType}
*/
public TargetType findOrCreateTargetType(final String targetTypeName) {
return targetTypeManagement.getByName(targetTypeName)
.orElseGet(() -> targetTypeManagement.create(entityFactory.targetType().create()
.name(targetTypeName).description(targetTypeName + " description").colour(DEFAULT_COLOUR)));
}
/**
* Creates {@link TargetType} in repository with given
* {@link TargetType#getName()}. Compatible distribution set types are assigned on creation
*
* @param targetTypeName
* {@link TargetType#getName()}
*
* @return persisted {@link TargetType}
*/
public TargetType createTargetType(final String targetTypeName, List<DistributionSetType> compatibleDsTypes) {
return targetTypeManagement.create(entityFactory.targetType().create()
.name(targetTypeName).description(targetTypeName + " description").colour(DEFAULT_COLOUR)
.compatible(compatibleDsTypes.stream().map(DistributionSetType::getId).collect(Collectors.toList())));
}
/**
* Creates {@link TargetType} in repository with given
* {@link TargetType#getName()}. No ds types are assigned on creation.
*
* @param targetTypePrefix
* {@link TargetType#getName()}
*
* @return persisted {@link TargetType}
*/
public List<TargetType> createTargetTypes(final String targetTypePrefix, int count) {
final List<TargetTypeCreate> result = Lists.newArrayListWithExpectedSize(count);
for (int i = 0; i < count; i++) {
result.add(entityFactory.targetType().create().name(targetTypePrefix + i).description(targetTypePrefix + " description")
.colour(DEFAULT_COLOUR));
}
return targetTypeManagement.create(result);
}
}