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:
@@ -29,6 +29,7 @@ Supported HTTP-methods are:
|
||||
Available Management APIs resources are:
|
||||
|
||||
- [Targets](/hawkbit/apis/mgmt/targets/)
|
||||
- [Target types](/hawkbit/apis/mgmt/targettypess/)
|
||||
- [Distribution sets](/hawkbit/apis/mgmt/distributionsets/)
|
||||
- [Distribution set types](/hawkbit/apis/mgmt/distributionsettypes/)
|
||||
- [Software modules](/hawkbit/apis/mgmt/softwaremodules/)
|
||||
|
||||
7
docs/content/apis/mgmt/targettypes.md
Normal file
7
docs/content/apis/mgmt/targettypes.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
title: Target Types API
|
||||
parent: Management API
|
||||
weight: -100
|
||||
---
|
||||
|
||||
<iframe width="100%" height="800px" frameborder="0" src="../../../rest-api/targettypes-api-guide/"></iframe>
|
||||
@@ -275,7 +275,9 @@ public enum SpServerError {
|
||||
*
|
||||
*/
|
||||
SP_NO_WEIGHT_PROVIDED_IN_MULTIASSIGNMENT_MODE("hawkbit.server.error.noWeightProvidedInMultiAssignmentMode",
|
||||
"The requested operation requires a weight to be specified when multi assignments is enabled.");
|
||||
"The requested operation requires a weight to be specified when multi assignments is enabled."),
|
||||
|
||||
SP_TARGET_TYPE_IN_USE("hawkbit.server.error.target.type.used", "Target type is still in use by a target.");
|
||||
|
||||
private final String key;
|
||||
private final String message;
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Describing the fields of the TargetType model which can be used in
|
||||
* the REST API
|
||||
*/
|
||||
public enum TargetTypeFields implements FieldNameProvider {
|
||||
/**
|
||||
* The name field.
|
||||
*/
|
||||
NAME("name"),
|
||||
/**
|
||||
* The description field.
|
||||
*/
|
||||
DESCRIPTION("description"),
|
||||
|
||||
/**
|
||||
* The id field.
|
||||
*/
|
||||
ID("id");
|
||||
|
||||
private final String fieldName;
|
||||
|
||||
TargetTypeFields(final String fieldName) {
|
||||
this.fieldName = fieldName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFieldName() {
|
||||
return fieldName;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -106,4 +106,9 @@ public interface QuotaManagement {
|
||||
*/
|
||||
long getMaxArtifactStorage();
|
||||
|
||||
/**
|
||||
* @return the maximum number of distribution set types per target type
|
||||
*/
|
||||
int getMaxDistributionSetTypesPerTargetType();
|
||||
|
||||
}
|
||||
|
||||
@@ -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}.
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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()}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 + "}.");
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -108,4 +108,8 @@ public class PropertiesQuotaManagement implements QuotaManagement {
|
||||
return securityProperties.getDos().getMaxArtifactStorage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxDistributionSetTypesPerTargetType() {
|
||||
return securityProperties.getDos().getMaxDistributionSetTypesPerTargetType();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)));
|
||||
};
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
@@ -76,6 +76,9 @@ public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest
|
||||
@Autowired
|
||||
protected TargetTagRepository targetTagRepository;
|
||||
|
||||
@Autowired
|
||||
protected TargetTypeRepository targetTypeRepository;
|
||||
|
||||
@Autowired
|
||||
protected DistributionSetTagRepository distributionSetTagRepository;
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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")));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* 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.mgmt.json.model.distributionsettype;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.MgmtId;
|
||||
|
||||
/**
|
||||
* Request Body of DistributionSetType for assignment operations (ID only).
|
||||
*
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class MgmtDistributionSetTypeAssignment extends MgmtId {
|
||||
}
|
||||
@@ -49,6 +49,24 @@ public class MgmtTarget extends MgmtNamedEntity {
|
||||
@JsonProperty
|
||||
private boolean requestAttributes;
|
||||
|
||||
@JsonProperty
|
||||
private Long targetType;
|
||||
|
||||
/**
|
||||
* @return Target type ID
|
||||
*/
|
||||
public Long getTargetType() {
|
||||
return targetType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param targetType
|
||||
* Target type ID
|
||||
*/
|
||||
public void setTargetType(Long targetType) {
|
||||
this.targetType = targetType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the controllerId
|
||||
*/
|
||||
@@ -150,6 +168,9 @@ public class MgmtTarget extends MgmtNamedEntity {
|
||||
return securityToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Address
|
||||
*/
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
@@ -171,6 +192,9 @@ public class MgmtTarget extends MgmtNamedEntity {
|
||||
this.securityToken = securityToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean true or false
|
||||
*/
|
||||
public boolean isRequestAttributes() {
|
||||
return requestAttributes;
|
||||
}
|
||||
|
||||
@@ -27,10 +27,34 @@ public class MgmtTargetRequestBody {
|
||||
@JsonProperty
|
||||
private Boolean requestAttributes;
|
||||
|
||||
@JsonProperty
|
||||
private Long targetType;
|
||||
|
||||
/**
|
||||
* @return Target type ID
|
||||
*/
|
||||
public Long getTargetType() {
|
||||
return targetType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param targetType
|
||||
* Target type ID
|
||||
*/
|
||||
public void setTargetType(Long targetType) {
|
||||
this.targetType = targetType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return token
|
||||
*/
|
||||
public String getSecurityToken() {
|
||||
return securityToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param securityToken Token
|
||||
*/
|
||||
public void setSecurityToken(final String securityToken) {
|
||||
this.securityToken = securityToken;
|
||||
}
|
||||
@@ -83,18 +107,32 @@ public class MgmtTargetRequestBody {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return address
|
||||
*/
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param address
|
||||
* Address
|
||||
*/
|
||||
public void setAddress(final String address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean true or false
|
||||
*/
|
||||
public Boolean isRequestAttributes() {
|
||||
return requestAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param requestAttributes
|
||||
* Attributes
|
||||
*/
|
||||
public void setRequestAttributes(final Boolean requestAttributes) {
|
||||
this.requestAttributes = requestAttributes;
|
||||
}
|
||||
|
||||
@@ -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.mgmt.json.model.targettype;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.MgmtNamedEntity;
|
||||
|
||||
/**
|
||||
* A json annotated rest model for TargetType to RESTful API
|
||||
* representation.
|
||||
*
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class MgmtTargetType extends MgmtNamedEntity {
|
||||
|
||||
@JsonProperty(value = "id", required = true)
|
||||
private Long typeId;
|
||||
|
||||
@JsonProperty
|
||||
private String colour;
|
||||
|
||||
/**
|
||||
* @return target type ID
|
||||
*/
|
||||
public Long getTypeId() {
|
||||
return typeId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param typeId
|
||||
* Target type ID
|
||||
*/
|
||||
public void setTypeId(final Long typeId) {
|
||||
this.typeId = typeId;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the color in format #000000
|
||||
*/
|
||||
public String getColour() {
|
||||
return colour;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param colour
|
||||
* in format #000000
|
||||
*/
|
||||
public void setColour(String colour) {
|
||||
this.colour = colour;
|
||||
}
|
||||
}
|
||||
@@ -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.mgmt.json.model.targettype;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.distributionsettype.MgmtDistributionSetTypeAssignment;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.distributionsettype.MgmtDistributionSetTypeRequestBodyPost;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.softwaremoduletype.MgmtSoftwareModuleTypeAssigment;
|
||||
|
||||
/**
|
||||
* Request Body for TargetType POST.
|
||||
*
|
||||
*/
|
||||
public class MgmtTargetTypeRequestBodyPost extends MgmtTargetTypeRequestBodyPut{
|
||||
|
||||
@JsonProperty
|
||||
private List<MgmtDistributionSetTypeAssignment> compatibledistributionsettypes;
|
||||
|
||||
/**
|
||||
* @param name
|
||||
* the name to set
|
||||
* @return post request body
|
||||
*/
|
||||
@Override
|
||||
public MgmtTargetTypeRequestBodyPost setName(final String name) {
|
||||
super.setName(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MgmtTargetTypeRequestBodyPost setDescription(final String description) {
|
||||
super.setDescription(description);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MgmtTargetTypeRequestBodyPost setColour(final String colour) {
|
||||
super.setColour(colour);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the compatible ds types
|
||||
*/
|
||||
public List<MgmtDistributionSetTypeAssignment> getCompatibleDsTypes() {
|
||||
return compatibledistributionsettypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param compatibleDsTypes
|
||||
* the compatible ds types to set
|
||||
*
|
||||
* @return updated body
|
||||
*/
|
||||
public MgmtTargetTypeRequestBodyPost setCompatibleDsTypes(
|
||||
final List<MgmtDistributionSetTypeAssignment> compatibleDsTypes) {
|
||||
this.compatibledistributionsettypes = compatibleDsTypes;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* 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.mgmt.json.model.targettype;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* Request Body for TargetType PUT.
|
||||
*
|
||||
*/
|
||||
public class MgmtTargetTypeRequestBodyPut {
|
||||
|
||||
@JsonProperty(required = true)
|
||||
private String name;
|
||||
|
||||
@JsonProperty
|
||||
private String description;
|
||||
|
||||
@JsonProperty
|
||||
private String colour;
|
||||
|
||||
/**
|
||||
* @return the name
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name
|
||||
* the name to set
|
||||
*
|
||||
* @return updated body
|
||||
*/
|
||||
public MgmtTargetTypeRequestBodyPut setName(final String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return description
|
||||
*/
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param description
|
||||
* Description
|
||||
* @return Updated body
|
||||
*/
|
||||
public MgmtTargetTypeRequestBodyPut setDescription(final String description) {
|
||||
this.description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Colour
|
||||
*/
|
||||
public String getColour() {
|
||||
return colour;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param colour
|
||||
* Colour
|
||||
* @return Updated body
|
||||
*/
|
||||
public MgmtTargetTypeRequestBodyPut setColour(final String colour) {
|
||||
this.colour = colour;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -60,6 +60,11 @@ public final class MgmtRestConstants {
|
||||
|
||||
public static final String SYSTEM_V1_REQUEST_MAPPING = BASE_V1_REQUEST_MAPPING + BASE_SYSTEM_MAPPING;
|
||||
|
||||
/**
|
||||
* The target URL mapping, href link for assigned target type.
|
||||
*/
|
||||
public static final String TARGET_V1_ASSIGNED_TARGET_TYPE= "targetType";
|
||||
|
||||
/**
|
||||
* The target URL mapping, href link for assigned distribution set.
|
||||
*/
|
||||
@@ -99,6 +104,21 @@ public final class MgmtRestConstants {
|
||||
*/
|
||||
public static final String TARGET_TAG_V1_REQUEST_MAPPING = BASE_V1_REQUEST_MAPPING + "/targettags";
|
||||
|
||||
/**
|
||||
* The target URL mapping rest resource.
|
||||
*/
|
||||
public static final String TARGET_TARGET_TYPE_V1_REQUEST_MAPPING = "/{targetId}/targettype";
|
||||
|
||||
/**
|
||||
* The target type URL mapping rest resource.
|
||||
*/
|
||||
public static final String TARGETTYPE_V1_REQUEST_MAPPING = BASE_V1_REQUEST_MAPPING + "/targettypes";
|
||||
|
||||
/**
|
||||
* The target type URL mapping rest resource.
|
||||
*/
|
||||
public static final String TARGETTYPE_V1_DS_TYPES = "compatibledistributionsettypes";
|
||||
|
||||
/**
|
||||
* The tag URL mapping rest resource.
|
||||
*
|
||||
|
||||
@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.mgmt.rest.api;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.hawkbit.mgmt.json.model.MgmtId;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.MgmtMetadata;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.MgmtMetadataBodyPut;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.PagedList;
|
||||
@@ -123,6 +124,31 @@ public interface MgmtTargetRestApi {
|
||||
@DeleteMapping(value = "/{targetId}")
|
||||
ResponseEntity<Void> deleteTarget(@PathVariable("targetId") String targetId);
|
||||
|
||||
/**
|
||||
* Handles the DELETE (unassign) request of a target type.
|
||||
*
|
||||
* @param targetId
|
||||
* the ID of the target
|
||||
* @return If the given targetId could exists and could be unassign Http OK.
|
||||
* In any failure the JsonResponseExceptionHandler is handling the
|
||||
* response.
|
||||
*/
|
||||
@DeleteMapping(value = MgmtRestConstants.TARGET_TARGET_TYPE_V1_REQUEST_MAPPING)
|
||||
ResponseEntity<Void> unassignTargetType(@PathVariable("targetId") String targetId);
|
||||
|
||||
/**
|
||||
* Handles the POST (assign) request of a target type.
|
||||
*
|
||||
* @param targetId
|
||||
* the ID of the target
|
||||
* @return If the given targetId could exists and could be assign Http OK.
|
||||
* In any failure the JsonResponseExceptionHandler is handling the
|
||||
* response.
|
||||
*/
|
||||
@PostMapping(value = MgmtRestConstants.TARGET_TARGET_TYPE_V1_REQUEST_MAPPING, consumes = { MediaTypes.HAL_JSON_VALUE,
|
||||
MediaType.APPLICATION_JSON_VALUE })
|
||||
ResponseEntity<Void> assignTargetType(@PathVariable("targetId") String targetId, MgmtId targetTypeId);
|
||||
|
||||
/**
|
||||
* Handles the GET request of retrieving the attributes of a specific
|
||||
* target.
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
/**
|
||||
* 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.mgmt.rest.api;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.hawkbit.mgmt.json.model.PagedList;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.distributionsettype.MgmtDistributionSetType;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.distributionsettype.MgmtDistributionSetTypeAssignment;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.targettype.MgmtTargetType;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.targettype.MgmtTargetTypeRequestBodyPost;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.targettype.MgmtTargetTypeRequestBodyPut;
|
||||
import org.springframework.hateoas.MediaTypes;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
/**
|
||||
* REST Resource handling for TargetType CRUD operations.
|
||||
*
|
||||
*/
|
||||
@RequestMapping(MgmtRestConstants.TARGETTYPE_V1_REQUEST_MAPPING)
|
||||
public interface MgmtTargetTypeRestApi {
|
||||
|
||||
/**
|
||||
* Handles the GET request of retrieving all TargetTypes.
|
||||
*
|
||||
* @param pagingOffsetParam
|
||||
* the offset of list of target types for pagination, might not be
|
||||
* present in the rest request then default value will be applied
|
||||
* @param pagingLimitParam
|
||||
* the limit of the paged request, might not be present in the rest
|
||||
* request then default value will be applied
|
||||
* @param sortParam
|
||||
* the sorting parameter in the request URL, syntax
|
||||
* {@code field:direction, field:direction}
|
||||
* @param rsqlParam
|
||||
* the search parameter in the request URL, syntax
|
||||
* {@code q=name==abc}
|
||||
*
|
||||
* @return a list of all TargetTypes for a defined or default page request with
|
||||
* status OK. The response is always paged. In any failure the
|
||||
* JsonResponseExceptionHandler is handling the response.
|
||||
*/
|
||||
@GetMapping(produces = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
|
||||
ResponseEntity<PagedList<MgmtTargetType>> getTargetTypes(
|
||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) int pagingOffsetParam,
|
||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) int pagingLimitParam,
|
||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) String sortParam,
|
||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) String rsqlParam);
|
||||
|
||||
/**
|
||||
* Handles the GET request of retrieving a single TargetType.
|
||||
*
|
||||
* @param targetTypeId
|
||||
* the ID of the target type to retrieve
|
||||
*
|
||||
* @return a single target type with status OK.
|
||||
*/
|
||||
@GetMapping(value = "/{targetTypeId}", produces = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
|
||||
ResponseEntity<MgmtTargetType> getTargetType(@PathVariable("targetTypeId") Long targetTypeId);
|
||||
|
||||
/**
|
||||
* Handles the DELETE request for a single Target Type.
|
||||
*
|
||||
* @param targetTypeId
|
||||
* the ID of the target type to retrieve
|
||||
* @return status OK if delete is successful.
|
||||
*
|
||||
*/
|
||||
@DeleteMapping(value = "/{targetTypeId}")
|
||||
ResponseEntity<Void> deleteTargetType(@PathVariable("targetTypeId") Long targetTypeId);
|
||||
|
||||
/**
|
||||
* Handles the PUT request of updating a Target Type.
|
||||
*
|
||||
* @param targetTypeId
|
||||
* the ID of the target type in the URL
|
||||
* @param restTargetType
|
||||
* the target type to be updated.
|
||||
* @return status OK if update is successful
|
||||
*/
|
||||
@PutMapping(value = "/{targetTypeId}", consumes = { MediaTypes.HAL_JSON_VALUE,
|
||||
MediaType.APPLICATION_JSON_VALUE }, produces = { MediaTypes.HAL_JSON_VALUE,
|
||||
MediaType.APPLICATION_JSON_VALUE })
|
||||
ResponseEntity<MgmtTargetType> updateTargetType(@PathVariable("targetTypeId") Long targetTypeId,
|
||||
MgmtTargetTypeRequestBodyPut restTargetType);
|
||||
|
||||
/**
|
||||
* Handles the POST request of creating new Target Types. The request body must
|
||||
* always be a list of types.
|
||||
*
|
||||
* @param targetTypes
|
||||
* the target types to be created.
|
||||
* @return In case all target types could be successfully created the
|
||||
* ResponseEntity with status code 201 - Created but without
|
||||
* ResponseBody. In any failure the JsonResponseExceptionHandler is
|
||||
* handling the response.
|
||||
*/
|
||||
@PostMapping(consumes = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE }, produces = {
|
||||
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
|
||||
ResponseEntity<List<MgmtTargetType>> createTargetTypes(List<MgmtTargetTypeRequestBodyPost> targetTypes);
|
||||
|
||||
/**
|
||||
* Handles the GET request of retrieving the list of compatible distribution set
|
||||
* types in that target type.
|
||||
*
|
||||
* @param targetTypeId
|
||||
* of the TargetType.
|
||||
* @return Unpaged list of distribution set types and OK in case of success.
|
||||
*/
|
||||
@GetMapping(value = "/{targetTypeId}/" + MgmtRestConstants.TARGETTYPE_V1_DS_TYPES, produces = {
|
||||
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
|
||||
ResponseEntity<List<MgmtDistributionSetType>> getCompatibleDistributionSets(
|
||||
@PathVariable("targetTypeId") Long targetTypeId);
|
||||
|
||||
/**
|
||||
* Handles DELETE request for removing the compatibility of a distribution set
|
||||
* type from the target type.
|
||||
*
|
||||
* @param targetTypeId
|
||||
* of the TargetType.
|
||||
* @param distributionSetTypeId
|
||||
* of the DistributionSetType.
|
||||
*
|
||||
* @return OK if the request was successful
|
||||
*/
|
||||
@DeleteMapping(value = "/{targetTypeId}/" + MgmtRestConstants.TARGETTYPE_V1_DS_TYPES + "/{distributionSetTypeId}")
|
||||
ResponseEntity<Void> removeCompatibleDistributionSet(@PathVariable("targetTypeId") Long targetTypeId,
|
||||
@PathVariable("distributionSetTypeId") Long distributionSetTypeId);
|
||||
|
||||
/**
|
||||
* Handles the POST request for adding the compatibility of a distribution set
|
||||
* type to a target type.
|
||||
*
|
||||
* @param targetTypeId
|
||||
* of the TargetType.
|
||||
* @param distributionSetTypeIds
|
||||
* of the DistributionSetTypes as a List.
|
||||
*
|
||||
* @return OK if the request was successful
|
||||
*/
|
||||
@PostMapping(value = "/{targetTypeId}/" + MgmtRestConstants.TARGETTYPE_V1_DS_TYPES, consumes = {
|
||||
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
|
||||
ResponseEntity<Void> addCompatibleDistributionSets(@PathVariable("targetTypeId") final Long targetTypeId,
|
||||
final List<MgmtDistributionSetTypeAssignment> distributionSetTypeIds);
|
||||
}
|
||||
@@ -67,7 +67,7 @@ final class MgmtDistributionSetTypeMapper {
|
||||
.orElse(Collections.emptyList());
|
||||
}
|
||||
|
||||
static List<MgmtDistributionSetType> toListResponse(final List<DistributionSetType> types) {
|
||||
static List<MgmtDistributionSetType> toListResponse(final Collection<DistributionSetType> types) {
|
||||
if (types == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.hawkbit.mgmt.json.model.MgmtMaintenanceWindow;
|
||||
@@ -30,6 +31,7 @@ import org.eclipse.hawkbit.mgmt.rest.api.MgmtDistributionSetRestApi;
|
||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRolloutRestApi;
|
||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetRestApi;
|
||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetTypeRestApi;
|
||||
import org.eclipse.hawkbit.repository.ActionFields;
|
||||
import org.eclipse.hawkbit.repository.ActionStatusFields;
|
||||
import org.eclipse.hawkbit.repository.DeploymentManagement;
|
||||
@@ -79,6 +81,10 @@ public final class MgmtTargetMapper {
|
||||
response.add(linkTo(methodOn(MgmtTargetRestApi.class).getMetadata(response.getControllerId(),
|
||||
MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET_VALUE,
|
||||
MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT_VALUE, null, null)).withRel("metadata"));
|
||||
if (response.getTargetType() != null) {
|
||||
response.add(linkTo(methodOn(MgmtTargetTypeRestApi.class)
|
||||
.getTargetType(response.getTargetType())).withRel(MgmtRestConstants.TARGET_V1_ASSIGNED_TARGET_TYPE));
|
||||
}
|
||||
}
|
||||
|
||||
static void addPollStatus(final Target target, final MgmtTarget targetRest) {
|
||||
@@ -153,6 +159,9 @@ public final class MgmtTargetMapper {
|
||||
if (installationDate != null) {
|
||||
targetRest.setInstalledAt(installationDate);
|
||||
}
|
||||
if (target.getTargetType() != null){
|
||||
targetRest.setTargetType(target.getTargetType().getId());
|
||||
}
|
||||
|
||||
targetRest.add(linkTo(methodOn(MgmtTargetRestApi.class).getTarget(target.getControllerId())).withSelfRel());
|
||||
|
||||
@@ -172,7 +181,7 @@ public final class MgmtTargetMapper {
|
||||
private static TargetCreate fromRequest(final EntityFactory entityFactory, final MgmtTargetRequestBody targetRest) {
|
||||
return entityFactory.target().create().controllerId(targetRest.getControllerId()).name(targetRest.getName())
|
||||
.description(targetRest.getDescription()).securityToken(targetRest.getSecurityToken())
|
||||
.address(targetRest.getAddress());
|
||||
.address(targetRest.getAddress()).targetType(targetRest.getTargetType());
|
||||
}
|
||||
|
||||
static List<MetaData> fromRequestTargetMetadata(final List<MgmtMetadata> metadata,
|
||||
|
||||
@@ -18,6 +18,7 @@ import java.util.stream.Collectors;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.ValidationException;
|
||||
|
||||
import org.eclipse.hawkbit.mgmt.json.model.MgmtId;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.MgmtMetadata;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.MgmtMetadataBodyPut;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.PagedList;
|
||||
@@ -140,7 +141,8 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
|
||||
|
||||
final Target updateTarget = this.targetManagement.update(entityFactory.target().update(targetId)
|
||||
.name(targetRest.getName()).description(targetRest.getDescription()).address(targetRest.getAddress())
|
||||
.securityToken(targetRest.getSecurityToken()).requestAttributes(targetRest.isRequestAttributes()));
|
||||
.targetType(targetRest.getTargetType()).securityToken(targetRest.getSecurityToken())
|
||||
.requestAttributes(targetRest.isRequestAttributes()));
|
||||
|
||||
final MgmtTarget response = MgmtTargetMapper.toResponse(updateTarget);
|
||||
MgmtTargetMapper.addPollStatus(updateTarget, response);
|
||||
@@ -156,6 +158,18 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Void> unassignTargetType(@PathVariable("targetId") final String targetId) {
|
||||
this.targetManagement.unAssignType(targetId);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Void> assignTargetType(@PathVariable("targetId") final String targetId, @RequestBody final MgmtId targetTypeId) {
|
||||
this.targetManagement.assignType(targetId, targetTypeId.getId());
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<MgmtTargetAttributes> getAttributes(@PathVariable("targetId") final String targetId) {
|
||||
final Map<String, String> controllerAttributes = targetManagement.getControllerAttributes(targetId);
|
||||
|
||||
@@ -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.mgmt.rest.resource;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.hawkbit.mgmt.json.model.distributionsettype.MgmtDistributionSetTypeAssignment;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.targettype.MgmtTargetType;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.targettype.MgmtTargetTypeRequestBodyPost;
|
||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetTypeRestApi;
|
||||
import org.eclipse.hawkbit.repository.EntityFactory;
|
||||
import org.eclipse.hawkbit.repository.builder.TargetTypeCreate;
|
||||
import org.eclipse.hawkbit.repository.model.TargetType;
|
||||
import org.eclipse.hawkbit.rest.data.ResponseList;
|
||||
|
||||
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
|
||||
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;
|
||||
|
||||
/**
|
||||
* A mapper which maps repository model to RESTful model representation and
|
||||
* back.
|
||||
*
|
||||
*/
|
||||
public final class MgmtTargetTypeMapper {
|
||||
|
||||
// private constructor, utility class
|
||||
private MgmtTargetTypeMapper() {
|
||||
}
|
||||
|
||||
static List<TargetTypeCreate> targetFromRequest(final EntityFactory entityFactory,
|
||||
final Collection<MgmtTargetTypeRequestBodyPost> targetTypesRest) {
|
||||
if (targetTypesRest == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return targetTypesRest.stream().map(targetRest -> fromRequest(entityFactory, targetRest)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static TargetTypeCreate fromRequest(final EntityFactory entityFactory,
|
||||
final MgmtTargetTypeRequestBodyPost targetTypesRest) {
|
||||
return entityFactory.targetType().create().name(targetTypesRest.getName()).description(targetTypesRest.getDescription())
|
||||
.colour(targetTypesRest.getColour()).compatible(getDistributionSets(targetTypesRest));
|
||||
}
|
||||
|
||||
private static Collection<Long> getDistributionSets(final MgmtTargetTypeRequestBodyPost targetTypesRest) {
|
||||
return Optional.ofNullable(targetTypesRest.getCompatibleDsTypes())
|
||||
.map(ds -> ds.stream().map(MgmtDistributionSetTypeAssignment::getId).collect(Collectors.toList()))
|
||||
.orElse(Collections.emptyList());
|
||||
}
|
||||
|
||||
static List<MgmtTargetType> toListResponse(final List<TargetType> types) {
|
||||
if (types == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return new ResponseList<>(types.stream().map(MgmtTargetTypeMapper::toResponse).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
static MgmtTargetType toResponse(final TargetType type) {
|
||||
final MgmtTargetType result = new MgmtTargetType();
|
||||
MgmtRestModelMapper.mapNamedToNamed(result, type);
|
||||
result.setTypeId(type.getId());
|
||||
result.setColour(type.getColour());
|
||||
result.add(linkTo(methodOn(MgmtTargetTypeRestApi.class).getTargetType(result.getTypeId())).withSelfRel());
|
||||
return result;
|
||||
}
|
||||
|
||||
static void addLinks(final MgmtTargetType result) {
|
||||
result.add(linkTo(methodOn(MgmtTargetTypeRestApi.class).getCompatibleDistributionSets(result.getTypeId()))
|
||||
.withRel(MgmtRestConstants.TARGETTYPE_V1_DS_TYPES));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
/**
|
||||
* 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.mgmt.rest.resource;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.hawkbit.mgmt.json.model.PagedList;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.distributionsettype.MgmtDistributionSetType;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.distributionsettype.MgmtDistributionSetTypeAssignment;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.targettype.MgmtTargetType;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.targettype.MgmtTargetTypeRequestBodyPost;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.targettype.MgmtTargetTypeRequestBodyPut;
|
||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetTypeRestApi;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetTypeManagement;
|
||||
import org.eclipse.hawkbit.repository.EntityFactory;
|
||||
import org.eclipse.hawkbit.repository.OffsetBasedPageRequest;
|
||||
import org.eclipse.hawkbit.repository.TargetTypeManagement;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.model.TargetType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* REST Resource handling for {@link TargetType} CRUD operations.
|
||||
*/
|
||||
@RestController
|
||||
public class MgmtTargetTypeResource implements MgmtTargetTypeRestApi {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(MgmtTargetTypeResource.class);
|
||||
|
||||
private final TargetTypeManagement targetTypeManagement;
|
||||
private final EntityFactory entityFactory;
|
||||
|
||||
public MgmtTargetTypeResource(TargetTypeManagement targetTypeManagement, final EntityFactory entityFactory) {
|
||||
this.targetTypeManagement = targetTypeManagement;
|
||||
this.entityFactory = entityFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<PagedList<MgmtTargetType>> getTargetTypes(
|
||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) final int pagingOffsetParam,
|
||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) final int pagingLimitParam,
|
||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) final String sortParam,
|
||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) final String rsqlParam) {
|
||||
|
||||
final int sanitizedOffsetParam = PagingUtility.sanitizeOffsetParam(pagingOffsetParam);
|
||||
final int sanitizedLimitParam = PagingUtility.sanitizePageLimitParam(pagingLimitParam);
|
||||
final Sort sorting = PagingUtility.sanitizeTargetTypeSortParam(sortParam);
|
||||
final Pageable pageable = new OffsetBasedPageRequest(sanitizedOffsetParam, sanitizedLimitParam, sorting);
|
||||
|
||||
final Page<TargetType> findTargetTypesAll;
|
||||
long countTargetTypesAll;
|
||||
if (rsqlParam != null) {
|
||||
findTargetTypesAll= targetTypeManagement.findByRsql(pageable, rsqlParam);
|
||||
} else {
|
||||
findTargetTypesAll = targetTypeManagement.findAll(pageable);
|
||||
}
|
||||
countTargetTypesAll = findTargetTypesAll.getTotalElements();
|
||||
|
||||
final List<MgmtTargetType> rest = MgmtTargetTypeMapper.toListResponse(findTargetTypesAll.getContent());
|
||||
return ResponseEntity.ok(new PagedList<>(rest, countTargetTypesAll));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<MgmtTargetType> getTargetType(@PathVariable("targetTypeId") final Long targetTypeId) {
|
||||
final TargetType foundType = findTargetTypeWithExceptionIfNotFound(targetTypeId);
|
||||
final MgmtTargetType response = MgmtTargetTypeMapper.toResponse(foundType);
|
||||
MgmtTargetTypeMapper.addLinks(response);
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Void> deleteTargetType(@PathVariable("targetTypeId") final Long targetTypeId) {
|
||||
LOG.debug("Delete {} target type", targetTypeId);
|
||||
targetTypeManagement.delete(targetTypeId);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<MgmtTargetType> updateTargetType(@PathVariable("targetTypeId") final Long targetTypeId,
|
||||
@RequestBody final MgmtTargetTypeRequestBodyPut restTargetType) {
|
||||
|
||||
final TargetType updated = targetTypeManagement
|
||||
.update(entityFactory.targetType().update(targetTypeId).name(restTargetType.getName())
|
||||
.description(restTargetType.getDescription()).colour(restTargetType.getColour()));
|
||||
final MgmtTargetType response = MgmtTargetTypeMapper.toResponse(updated);
|
||||
MgmtTargetTypeMapper.addLinks(response);
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<List<MgmtTargetType>> createTargetTypes(
|
||||
@RequestBody final List<MgmtTargetTypeRequestBodyPost> targetTypes) {
|
||||
|
||||
final List<TargetType> createdTargetTypes = targetTypeManagement
|
||||
.create(MgmtTargetTypeMapper.targetFromRequest(entityFactory, targetTypes));
|
||||
return ResponseEntity.status(HttpStatus.CREATED).body(MgmtTargetTypeMapper.toListResponse(createdTargetTypes));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<List<MgmtDistributionSetType>> getCompatibleDistributionSets(
|
||||
@PathVariable("targetTypeId") final Long targetTypeId) {
|
||||
|
||||
final TargetType foundType = findTargetTypeWithExceptionIfNotFound(targetTypeId);
|
||||
return ResponseEntity
|
||||
.ok(MgmtDistributionSetTypeMapper.toListResponse(foundType.getCompatibleDistributionSetTypes()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Void> removeCompatibleDistributionSet(@PathVariable("targetTypeId") final Long targetTypeId,
|
||||
@PathVariable("distributionSetTypeId") final Long distributionSetTypeId) {
|
||||
|
||||
targetTypeManagement.unassignDistributionSetType(targetTypeId, distributionSetTypeId);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Void> addCompatibleDistributionSets(@PathVariable("targetTypeId") final Long targetTypeId,
|
||||
@RequestBody final List<MgmtDistributionSetTypeAssignment> distributionSetTypeIds) {
|
||||
|
||||
targetTypeManagement.assignCompatibleDistributionSetTypes(targetTypeId,
|
||||
distributionSetTypeIds.stream().map(MgmtDistributionSetTypeAssignment::getId).collect(Collectors.toList()));
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
private TargetType findTargetTypeWithExceptionIfNotFound(final Long targetTypeId) {
|
||||
return targetTypeManagement.get(targetTypeId)
|
||||
.orElseThrow(() -> new EntityNotFoundException(TargetType.class, targetTypeId));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import org.eclipse.hawkbit.repository.SoftwareModuleTypeFields;
|
||||
import org.eclipse.hawkbit.repository.TagFields;
|
||||
import org.eclipse.hawkbit.repository.TargetFields;
|
||||
import org.eclipse.hawkbit.repository.TargetFilterQueryFields;
|
||||
import org.eclipse.hawkbit.repository.TargetTypeFields;
|
||||
import org.eclipse.hawkbit.rest.util.SortUtility;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.domain.Sort.Direction;
|
||||
@@ -61,6 +62,14 @@ public final class PagingUtility {
|
||||
return Sort.by(SortUtility.parse(TargetFields.class, sortParam));
|
||||
}
|
||||
|
||||
static Sort sanitizeTargetTypeSortParam(final String sortParam) {
|
||||
if (sortParam == null) {
|
||||
// default
|
||||
return Sort.by(Direction.ASC, TargetTypeFields.ID.getFieldName());
|
||||
}
|
||||
return Sort.by(SortUtility.parse(TargetFields.class, sortParam));
|
||||
}
|
||||
|
||||
static Sort sanitizeTagSortParam(final String sortParam) {
|
||||
if (sortParam == null) {
|
||||
// default
|
||||
|
||||
@@ -33,6 +33,7 @@ import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.validation.ConstraintViolationException;
|
||||
@@ -56,6 +57,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.TargetMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.TargetType;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
import org.eclipse.hawkbit.repository.test.util.WithUser;
|
||||
import org.eclipse.hawkbit.rest.exception.MessageNotReadableException;
|
||||
@@ -63,6 +65,7 @@ import org.eclipse.hawkbit.rest.json.model.ExceptionInfo;
|
||||
import org.eclipse.hawkbit.rest.util.JsonBuilder;
|
||||
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
|
||||
import org.eclipse.hawkbit.util.IpUtil;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -105,6 +108,7 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
|
||||
private static final String JSON_PATH_FIELD_SIZE = ".size";
|
||||
private static final String JSON_PATH_FIELD_TOTAL = ".total";
|
||||
private static final String JSON_PATH_FIELD_LAST_REQUEST_AT = ".lastControllerRequestAt";
|
||||
private static final String JSON_PATH_FIELD_TARGET_TYPE = ".targetType";
|
||||
|
||||
// target
|
||||
// $.field
|
||||
@@ -117,6 +121,7 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
|
||||
private static final String JSON_PATH_CONTROLLERID = JSON_PATH_ROOT + JSON_PATH_FIELD_CONTROLLERID;
|
||||
private static final String JSON_PATH_DESCRIPTION = JSON_PATH_ROOT + JSON_PATH_FIELD_DESCRIPTION;
|
||||
private static final String JSON_PATH_LAST_REQUEST_AT = JSON_PATH_ROOT + JSON_PATH_FIELD_LAST_REQUEST_AT;
|
||||
private static final String JSON_PATH_TYPE = JSON_PATH_ROOT + JSON_PATH_FIELD_TARGET_TYPE;
|
||||
|
||||
@Autowired
|
||||
private JpaProperties jpaProperties;
|
||||
@@ -2075,4 +2080,192 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
|
||||
"$._links.rollout.href", containsString("/rest/v1/rollouts/" + rollout.getId().intValue())));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Ensures that a post request for creating targets with target type works.")
|
||||
public void createTargetsWithTargetType() throws Exception {
|
||||
final TargetType type1 = testdataFactory.createTargetType("typeWithDs", Collections.singletonList(standardDsType));
|
||||
final TargetType type2 = testdataFactory.createTargetType("typeWithOutDs", Collections.singletonList(standardDsType));
|
||||
|
||||
final Target test1 = entityFactory.target().create().controllerId("id1").name("targetWithoutType")
|
||||
.securityToken("token").address("amqp://test123/foobar").description("testid1").build();
|
||||
final Target test2 = entityFactory.target().create().controllerId("id2").name("targetOfType1")
|
||||
.targetType(type1.getId()).description("testid2").build();
|
||||
final Target test3 = entityFactory.target().create().controllerId("id3").name("targetOfType2")
|
||||
.targetType(type2.getId()).description("testid3").build();
|
||||
final String hrefType1 = "http://localhost/rest/v1/targettypes/" + type1.getId();
|
||||
|
||||
final List<Target> targets = Arrays.asList(test1, test2, test3);
|
||||
|
||||
final MvcResult mvcPostResult = mvc
|
||||
.perform(post("/rest/v1/targets/").content(JsonBuilder.targets(targets, true))
|
||||
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isCreated())
|
||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
|
||||
.andExpect(jsonPath("[0].name", equalTo("targetWithoutType")))
|
||||
.andExpect(jsonPath("[0].controllerId", equalTo("id1")))
|
||||
.andExpect(jsonPath("[0].description", equalTo("testid1")))
|
||||
.andExpect(jsonPath("[0].createdAt", not(equalTo(0))))
|
||||
.andExpect(jsonPath("[0].createdBy", equalTo("bumlux")))
|
||||
.andExpect(jsonPath("[0].securityToken", equalTo("token")))
|
||||
.andExpect(jsonPath("[0].address", equalTo("amqp://test123/foobar")))
|
||||
.andExpect(jsonPath("[0].targetType").doesNotExist())
|
||||
.andExpect(jsonPath("[1].name", equalTo("targetOfType1")))
|
||||
.andExpect(jsonPath("[1].createdBy", equalTo("bumlux")))
|
||||
.andExpect(jsonPath("[1].controllerId", equalTo("id2")))
|
||||
.andExpect(jsonPath("[1].description", equalTo("testid2")))
|
||||
.andExpect(jsonPath("[1].createdAt", not(equalTo(0))))
|
||||
.andExpect(jsonPath("[1].createdBy", equalTo("bumlux")))
|
||||
.andExpect(jsonPath("[1].targetType", equalTo(type1.getId().intValue())))
|
||||
.andExpect(jsonPath("[2].name", equalTo("targetOfType2")))
|
||||
.andExpect(jsonPath("[2].controllerId", equalTo("id3")))
|
||||
.andExpect(jsonPath("[2].description", equalTo("testid3")))
|
||||
.andExpect(jsonPath("[2].createdAt", not(equalTo(0))))
|
||||
.andExpect(jsonPath("[2].createdBy", equalTo("bumlux")))
|
||||
.andExpect(jsonPath("[2].targetType", equalTo(type2.getId().intValue())))
|
||||
.andReturn();
|
||||
|
||||
mvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + test2.getControllerId()))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(jsonPath(JSON_PATH_NAME, equalTo("targetOfType1")))
|
||||
.andExpect(jsonPath(JSON_PATH_CONTROLLERID, equalTo("id2")))
|
||||
.andExpect(jsonPath(JSON_PATH_TYPE, equalTo(type1.getId().intValue())))
|
||||
.andExpect(jsonPath(JSON_PATH_DESCRIPTION, equalTo("testid2")))
|
||||
.andExpect(jsonPath("$._links.targetType.href", equalTo(hrefType1))).andReturn();
|
||||
|
||||
assertThat(targetManagement.getByControllerID("id1")).isNotNull();
|
||||
assertThat(targetManagement.getByControllerID("id1").get().getName()).isEqualTo("targetWithoutType");
|
||||
assertThat(targetManagement.getByControllerID("id1").get().getDescription()).isEqualTo("testid1");
|
||||
assertThat(targetManagement.getByControllerID("id1").get().getTargetType()).isNull();
|
||||
assertThat(targetManagement.getByControllerID("id1").get().getSecurityToken()).isEqualTo("token");
|
||||
assertThat(targetManagement.getByControllerID("id1").get().getAddress().toString())
|
||||
.isEqualTo("amqp://test123/foobar");
|
||||
assertThat(targetManagement.getByControllerID("id2")).isNotNull();
|
||||
assertThat(targetManagement.getByControllerID("id2").get().getName()).isEqualTo("targetOfType1");
|
||||
assertThat(targetManagement.getByControllerID("id2").get().getDescription()).isEqualTo("testid2");
|
||||
assertThat(targetManagement.getByControllerID("id2").get().getTargetType().getName()).isEqualTo("typeWithDs");
|
||||
assertThat(targetManagement.getByControllerID("id3")).isNotNull();
|
||||
assertThat(targetManagement.getByControllerID("id3").get().getName()).isEqualTo("targetOfType2");
|
||||
assertThat(targetManagement.getByControllerID("id3").get().getDescription()).isEqualTo("testid3");
|
||||
assertThat(targetManagement.getByControllerID("id3").get().getTargetType().getName()).isEqualTo("typeWithOutDs");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Ensures that a post request for creating target with target type works.")
|
||||
public void createTargetWithExistingTargetType() throws Exception {
|
||||
// create target type
|
||||
List<TargetType> targetTypes = testdataFactory.createTargetTypes("targettype",1);
|
||||
assertThat(targetTypes).hasSize(1);
|
||||
|
||||
final Target target = entityFactory.target().create().controllerId("targetcontroller").name("testtarget").targetType(targetTypes.get(0).getId()).build();
|
||||
|
||||
final String targetList = JsonBuilder.targets(Collections.singletonList(target), false);
|
||||
|
||||
// test query target over rest resource
|
||||
mvc.perform(post(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING).content(targetList)
|
||||
.contentType(MediaType.APPLICATION_JSON)).andExpect(status().isCreated())
|
||||
.andExpect(jsonPath("[0].controllerId", equalTo("targetcontroller")))
|
||||
.andExpect(jsonPath("[0].targetType", equalTo(targetTypes.get(0).getId().intValue())));
|
||||
|
||||
assertThat(targetManagement.getByControllerID("targetcontroller").get().getTargetType().getId()).isEqualTo(targetTypes.get(0).getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Ensures that a put request for updating targets with target type works.")
|
||||
public void updateTargetTypeInTarget() throws Exception {
|
||||
// create target type
|
||||
List<TargetType> targetTypes = testdataFactory.createTargetTypes("targettype",2);
|
||||
assertThat(targetTypes).hasSize(2);
|
||||
|
||||
String controllerId = "targetcontroller";
|
||||
Target target = testdataFactory.createTarget(controllerId, "testtarget", targetTypes.get(0).getId());
|
||||
|
||||
assertThat(target).isNotNull();
|
||||
assertThat(target.getTargetType().getId()).isEqualTo(targetTypes.get(0).getId());
|
||||
|
||||
// update target over rest resource
|
||||
final String body = new JSONObject().put("targetType", targetTypes.get(1).getId().intValue()).toString();
|
||||
|
||||
mvc.perform(put(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + controllerId).content(body)
|
||||
.contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk())
|
||||
.andExpect(jsonPath("controllerId", equalTo(controllerId)))
|
||||
.andExpect(jsonPath("targetType", equalTo(targetTypes.get(1).getId().intValue())));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Ensures that a post request for creating targets with unknown target type fails.")
|
||||
public void addingNonExistingTargetTypeInTargetShouldFail() throws Exception {
|
||||
long unknownTargetTypeId = 999;
|
||||
String errorMsg = String.format("TargetType with given identifier {%s} does not exist.", unknownTargetTypeId);
|
||||
|
||||
Optional<TargetType> targetType = targetTypeManagement.get(unknownTargetTypeId);
|
||||
assertThat(targetType).isNotPresent();
|
||||
|
||||
String controllerId = "targetcontroller";
|
||||
final Target target = entityFactory.target().create().controllerId(controllerId).name("testtarget").build();
|
||||
|
||||
final String targetList = JsonBuilder.targets(Collections.singletonList(target), false, unknownTargetTypeId);
|
||||
|
||||
// post target over rest resource
|
||||
mvc.perform(post(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING).content(targetList)
|
||||
.contentType(MediaType.APPLICATION_JSON)).andExpect(status().isNotFound())
|
||||
.andExpect(jsonPath("message", Matchers.containsString(errorMsg)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Ensures that a post request for assign target type to target works.")
|
||||
public void assignTargetTypeToTarget() throws Exception {
|
||||
// create target type
|
||||
TargetType targetType = testdataFactory.findOrCreateTargetType("targettype");
|
||||
assertThat(targetType).isNotNull();
|
||||
|
||||
// create target
|
||||
String targetControllerId = "targetcontroller";
|
||||
Target target = testdataFactory.createTarget(targetControllerId, "testtarget");
|
||||
assertThat(target).isNotNull();
|
||||
|
||||
// assign target type over rest resource
|
||||
mvc.perform(post(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + targetControllerId + "/targettype")
|
||||
.content("{\"id\":" + targetType.getId() + "}").contentType(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
|
||||
|
||||
assertThat(targetManagement.getByControllerID(targetControllerId).get().getTargetType().getId()).isEqualTo(targetType.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Ensures that a post request for assign a invalid target type to target fails.")
|
||||
public void assignInvalidTargetTypeToTargetFails() throws Exception {
|
||||
// Invalid target type ID
|
||||
long invalidTargetTypeId = 999;
|
||||
|
||||
// create target
|
||||
String targetControllerId = "targetcontroller";
|
||||
Target target = testdataFactory.createTarget(targetControllerId, "testtarget");
|
||||
assertThat(target).isNotNull();
|
||||
|
||||
// assign invalid target type over rest resource
|
||||
mvc.perform(post(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + targetControllerId + "/targettype")
|
||||
.content("{\"id\":" + invalidTargetTypeId + "}").contentType(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Ensures that a delete request for unassign target type from target works.")
|
||||
public void unassignTargetTypeFromTarget() throws Exception {
|
||||
// create target type
|
||||
List<TargetType> targetTypes = testdataFactory.createTargetTypes("targettype",1);
|
||||
assertThat(targetTypes).hasSize(1);
|
||||
|
||||
String targetControllerId = "targetcontroller";
|
||||
Target target = testdataFactory.createTarget(targetControllerId, "testtarget", targetTypes.get(0).getId());
|
||||
|
||||
assertThat(target).isNotNull();
|
||||
assertThat(target.getTargetType().getId()).isEqualTo(targetTypes.get(0).getId());
|
||||
|
||||
// unassign target type over rest resource
|
||||
mvc.perform(delete(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + targetControllerId + "/targettype")
|
||||
.contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk());
|
||||
|
||||
assertThat(targetManagement.getByControllerID(targetControllerId).get().getTargetType()).isNull();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,616 @@
|
||||
/**
|
||||
* 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.mgmt.rest.resource;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import com.jayway.jsonpath.JsonPath;
|
||||
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.im.authentication.SpPermission;
|
||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
||||
import org.eclipse.hawkbit.repository.builder.TargetTypeCreate;
|
||||
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.util.WithUser;
|
||||
import org.eclipse.hawkbit.rest.util.JsonBuilder;
|
||||
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
|
||||
import org.json.JSONObject;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
import org.springframework.test.web.servlet.ResultActions;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.hamcrest.Matchers.hasToString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Spring MVC Tests against the MgmtTargetTypeResource.
|
||||
*
|
||||
*/
|
||||
@Feature("Component Tests - Management API")
|
||||
@Story("Target Type Resource")
|
||||
class MgmtTargetTypeResourceTest extends AbstractManagementApiIntegrationTest {
|
||||
|
||||
private final static String TARGETTYPES_ENDPOINT = MgmtRestConstants.TARGETTYPE_V1_REQUEST_MAPPING;
|
||||
private final static String TARGETTYPE_SINGLE_ENDPOINT = MgmtRestConstants.TARGETTYPE_V1_REQUEST_MAPPING
|
||||
+ "/{typeid}";
|
||||
private final static String TARGETTYPE_DSTYPES_ENDPOINT = TARGETTYPE_SINGLE_ENDPOINT + "/"
|
||||
+ MgmtRestConstants.TARGETTYPE_V1_DS_TYPES;
|
||||
private final static String TARGETTYPE_DSTYPE_SINGLE_ENDPOINT = TARGETTYPE_DSTYPES_ENDPOINT + "/{dstypeid}";
|
||||
|
||||
private final static String TEST_USER = "targetTypeTester";
|
||||
|
||||
|
||||
@Test
|
||||
@WithUser(principal = "targetTypeTester", allSpPermissions = true, removeFromAllPermission = { SpPermission.READ_TARGET })
|
||||
@Description("GET targettypes returns Forbidden when permission is missing")
|
||||
void getTargetTypesWithoutPermission() throws Exception {
|
||||
mvc.perform(get(TARGETTYPES_ENDPOINT).accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isForbidden());
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithUser(principal = TEST_USER, allSpPermissions = true)
|
||||
@Description("Checks the correct behaviour of /rest/v1/targettypes/{id} GET request.")
|
||||
void getTargetType() throws Exception {
|
||||
String typeName = "TestTypeGET";
|
||||
TargetType testType = createTestTargetTypeInDB(typeName);
|
||||
Long typeId = testType.getId();
|
||||
|
||||
mvc.perform(get(TARGETTYPE_SINGLE_ENDPOINT, typeId).accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
|
||||
.andExpect(jsonPath("$.id", is(typeId), Long.class)).andExpect(jsonPath("$.name", equalTo(typeName)))
|
||||
.andExpect(jsonPath("$.colour", is("#000000")))
|
||||
.andExpect(jsonPath("$.description", equalTo(typeName + " description")))
|
||||
.andExpect(jsonPath("$.createdBy", equalTo(TEST_USER)))
|
||||
.andExpect(jsonPath("$.createdAt", equalTo(testType.getCreatedAt())))
|
||||
.andExpect(jsonPath("$.lastModifiedBy", equalTo(TEST_USER)))
|
||||
.andExpect(jsonPath("$.lastModifiedAt", equalTo(testType.getLastModifiedAt())))
|
||||
.andExpect(jsonPath("$._links.self.href", equalTo("http://localhost/rest/v1/targettypes/" + typeId)))
|
||||
.andExpect(jsonPath("$.deleted").doesNotExist())
|
||||
.andExpect(jsonPath("$.key").doesNotExist())
|
||||
.andExpect(jsonPath("$._links.compatibledistributionsettypes.href",
|
||||
equalTo("http://localhost/rest/v1/targettypes/" + typeId + "/compatibledistributionsettypes")));
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithUser(principal = TEST_USER, allSpPermissions = true)
|
||||
@Description("Checks the correct behaviour of /rest/v1/targettypes GET requests.")
|
||||
void getTargetTypes() throws Exception {
|
||||
String typeName = "TestTypeGET";
|
||||
int count = 5;
|
||||
List<TargetType> testTypes = createTestTargetTypesInDB(typeName, count);
|
||||
|
||||
ResultActions resultActions = mvc.perform(get(TARGETTYPES_ENDPOINT).accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print());
|
||||
|
||||
for (int index = 0; index < count; index++) {
|
||||
Long typeId = testTypes.get(index).getId();
|
||||
resultActions.andExpect(status().isOk()).andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
|
||||
.andExpect(jsonPath("$.content.[?(@.id=='" + typeId + "')].id", contains(typeId.intValue())))
|
||||
.andExpect(jsonPath("$.content.[?(@.id=='" + typeId + "')].name", contains(typeName + index)))
|
||||
.andExpect(jsonPath("$.content.[?(@.id=='" + typeId + "')].colour", contains("#000000")))
|
||||
.andExpect(jsonPath("$.content.[?(@.id=='" + typeId + "')].description",
|
||||
contains(typeName + " description")))
|
||||
.andExpect(jsonPath("$.content.[?(@.id=='" + typeId + "')].createdBy", contains(TEST_USER)))
|
||||
.andExpect(jsonPath("$.content.[?(@.id=='" + typeId + "')].createdAt",
|
||||
contains(testTypes.get(index).getCreatedAt())))
|
||||
.andExpect(jsonPath("$.content.[?(@.id=='" + typeId + "')].lastModifiedBy", contains(TEST_USER)))
|
||||
.andExpect(jsonPath("$.content.[?(@.id=='" + typeId + "')].lastModifiedAt",
|
||||
contains(testTypes.get(index).getLastModifiedAt())))
|
||||
.andExpect(jsonPath("$.content.[?(@.id=='" + typeId + "')].deleted").doesNotExist())
|
||||
.andExpect(jsonPath("$.content.[?(@.id=='" + typeId + "')].key").doesNotExist())
|
||||
.andExpect(jsonPath("$.content.[?(@.id=='" + typeId + "')]._links.self.href",
|
||||
contains("http://localhost/rest/v1/targettypes/" + typeId)))
|
||||
.andExpect(
|
||||
jsonPath("$.content.[?(@.id=='" + typeId + "')]._links.compatibledistributionsettypes.href")
|
||||
.doesNotExist())
|
||||
.andExpect(jsonPath("$.total", equalTo(count))).andExpect(jsonPath("$.size", equalTo(count)));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithUser(principal = TEST_USER, allSpPermissions = true)
|
||||
@Description("Checks the correct behaviour of /rest/v1/targettypes GET requests without prior created target types.")
|
||||
void getDefaultTargetTypes() throws Exception {
|
||||
|
||||
// 0 types overall (no default types are created)
|
||||
final int types = 0;
|
||||
mvc.perform(get(TARGETTYPES_ENDPOINT)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(jsonPath(MgmtTargetResourceTest.JSON_PATH_PAGED_LIST_TOTAL, equalTo(types)))
|
||||
.andExpect(jsonPath(MgmtTargetResourceTest.JSON_PATH_PAGED_LIST_SIZE, equalTo(types)))
|
||||
.andExpect(jsonPath(MgmtTargetResourceTest.JSON_PATH_PAGED_LIST_CONTENT, hasSize(types)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithUser(principal = TEST_USER, allSpPermissions = true)
|
||||
@Description("Checks the correct behaviour of /rest/v1/targettypes GET requests with sorting by name.")
|
||||
void getTargetTypesSortedByName() throws Exception {
|
||||
String typeNameA = "ATestTypeGETsorted";
|
||||
String typeNameB = "BTestTypeGETsorted";
|
||||
String typeNameC = "CTestTypeGETsorted";
|
||||
TargetType testTypeB = createTestTargetTypeInDB(typeNameB);
|
||||
TargetType testTypeC = createTestTargetTypeInDB(typeNameC);
|
||||
TargetType testTypeA = createTestTargetTypeInDB(typeNameA);
|
||||
|
||||
testTypeA = targetTypeManagement
|
||||
.update(entityFactory.targetType().update(testTypeA.getId()).description("Updated description"));
|
||||
|
||||
// descending
|
||||
mvc.perform(get(TARGETTYPES_ENDPOINT).accept(MediaType.APPLICATION_JSON)
|
||||
.param(MgmtRestConstants.REQUEST_PARAMETER_SORTING, "name:DESC")).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isOk()).andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
|
||||
.andExpect(jsonPath("$.content.[0].id", equalTo(testTypeC.getId().intValue())))
|
||||
.andExpect(jsonPath("$.content.[0].name", equalTo(typeNameC)))
|
||||
.andExpect(jsonPath("$.content.[0].colour", equalTo("#000000")))
|
||||
.andExpect(jsonPath("$.content.[0].description", equalTo(typeNameC + " description")))
|
||||
.andExpect(jsonPath("$.content.[0].createdBy", equalTo(TEST_USER)))
|
||||
.andExpect(jsonPath("$.content.[0].createdAt", equalTo(testTypeC.getCreatedAt())))
|
||||
.andExpect(jsonPath("$.content.[0].lastModifiedBy", equalTo(TEST_USER)))
|
||||
.andExpect(jsonPath("$.content.[0].lastModifiedAt", equalTo(testTypeC.getLastModifiedAt())))
|
||||
.andExpect(jsonPath("$.content.[0].deleted").doesNotExist())
|
||||
.andExpect(jsonPath("$.content.[0].key").doesNotExist())
|
||||
.andExpect(jsonPath("$.content.[0]._links.self.href",
|
||||
equalTo("http://localhost/rest/v1/targettypes/" + testTypeC.getId())))
|
||||
.andExpect(jsonPath("$.content.[0]._links.compatibledistributionsettypes.href").doesNotExist())
|
||||
.andExpect(jsonPath("$.total", equalTo(3))).andExpect(jsonPath("$.size", equalTo(3)))
|
||||
.andExpect(jsonPath("$.content.[1].name", equalTo(typeNameB)))
|
||||
.andExpect(jsonPath("$.content.[2].name", equalTo(typeNameA)));
|
||||
|
||||
// ascending
|
||||
mvc.perform(get(TARGETTYPES_ENDPOINT).accept(MediaType.APPLICATION_JSON)
|
||||
.param(MgmtRestConstants.REQUEST_PARAMETER_SORTING, "name:ASC")).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isOk()).andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
|
||||
.andExpect(jsonPath("$.content.[0].id", equalTo(testTypeA.getId().intValue())))
|
||||
.andExpect(jsonPath("$.content.[0].name", equalTo(typeNameA)))
|
||||
.andExpect(jsonPath("$.content.[0].description", equalTo("Updated description")))
|
||||
.andExpect(jsonPath("$.content.[0].colour", equalTo("#000000")))
|
||||
.andExpect(jsonPath("$.content.[0].createdBy", equalTo(TEST_USER)))
|
||||
.andExpect(jsonPath("$.content.[0].createdAt", equalTo(testTypeA.getCreatedAt())))
|
||||
.andExpect(jsonPath("$.content.[0].lastModifiedBy", equalTo(TEST_USER)))
|
||||
.andExpect(jsonPath("$.content.[0].lastModifiedAt", equalTo(testTypeA.getLastModifiedAt())))
|
||||
.andExpect(jsonPath("$.content.[0].deleted").doesNotExist())
|
||||
.andExpect(jsonPath("$.content.[0].key").doesNotExist())
|
||||
.andExpect(jsonPath("$.content.[0]._links.self.href",
|
||||
equalTo("http://localhost/rest/v1/targettypes/" + testTypeA.getId())))
|
||||
.andExpect(jsonPath("$.content.[0]._links.compatibledistributionsettypes.href").doesNotExist())
|
||||
.andExpect(jsonPath("$.total", equalTo(3))).andExpect(jsonPath("$.size", equalTo(3)))
|
||||
.andExpect(jsonPath("$.content.[1].name", equalTo(typeNameB)))
|
||||
.andExpect(jsonPath("$.content.[2].name", equalTo(typeNameC)));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithUser(principal = TEST_USER, allSpPermissions = true)
|
||||
@Description("Checks the correct behaviour of /rest/v1/targettypes GET requests with paging.")
|
||||
void getTargetTypesWithPagingLimitRequestParameter() throws Exception {
|
||||
final String typePrefix = "TestTypeGETPaging";
|
||||
final int count = 10;
|
||||
final int limit = 3;
|
||||
createTestTargetTypesInDB(typePrefix, count);
|
||||
|
||||
mvc.perform(get(TARGETTYPES_ENDPOINT).param(MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT,
|
||||
String.valueOf(limit))).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(jsonPath(MgmtTargetResourceTest.JSON_PATH_PAGED_LIST_TOTAL, equalTo(count)))
|
||||
.andExpect(jsonPath(MgmtTargetResourceTest.JSON_PATH_PAGED_LIST_SIZE, equalTo(limit)))
|
||||
.andExpect(jsonPath(MgmtTargetResourceTest.JSON_PATH_PAGED_LIST_CONTENT, hasSize(limit)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithUser(principal = TEST_USER, allSpPermissions = true)
|
||||
@Description("Checks the correct behaviour of /rest/v1/targettypes GET requests with paging and offset.")
|
||||
void getTargetTypesWithPagingLimitAndOffsetRequestParameter() throws Exception {
|
||||
final int count = 10;
|
||||
final int offset = 2;
|
||||
final int expectedSize = count - offset;
|
||||
final String typePrefix = "TestTypeGETPaging";
|
||||
createTestTargetTypesInDB(typePrefix, count);
|
||||
|
||||
mvc.perform(get(TARGETTYPES_ENDPOINT)
|
||||
.param(MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, String.valueOf(offset))
|
||||
.param(MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, String.valueOf(count)))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(jsonPath(MgmtTargetResourceTest.JSON_PATH_PAGED_LIST_TOTAL, equalTo(count)))
|
||||
.andExpect(jsonPath(MgmtTargetResourceTest.JSON_PATH_PAGED_LIST_SIZE, equalTo(expectedSize)))
|
||||
.andExpect(jsonPath(MgmtTargetResourceTest.JSON_PATH_PAGED_LIST_CONTENT, hasSize(expectedSize)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithUser(principal = TEST_USER, allSpPermissions = true)
|
||||
@Description("Checks the correct behaviour of /rest/v1/targettypes/{ID} PUT requests.")
|
||||
void updateTargetType() throws Exception {
|
||||
String typeName = "TestTypePUT";
|
||||
final TargetType testType = createTestTargetTypeInDB(typeName);
|
||||
final String body = new JSONObject().put("id", testType.getId()).put("description", "updated description")
|
||||
.put("name", "TestTypePUTupdated").put("colour", "#ffffff").toString();
|
||||
|
||||
mvc.perform(
|
||||
put(TARGETTYPE_SINGLE_ENDPOINT, testType.getId()).content(body).contentType(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.id", equalTo(testType.getId().intValue())))
|
||||
.andExpect(jsonPath("$.description", equalTo("updated description")))
|
||||
.andExpect(jsonPath("$.name", equalTo("TestTypePUTupdated")))
|
||||
.andExpect(jsonPath("$.colour", equalTo("#ffffff"))).andReturn();
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithUser(principal = TEST_USER, allSpPermissions = true)
|
||||
@Description("Checks the correct behaviour of /rest/v1/targettypes/{id} GET requests.")
|
||||
void getUpdatedTargetType() throws Exception {
|
||||
TargetType testType = createTestTargetTypeInDB("TestTypeGET");
|
||||
String typeNameUpdated = "TestTypeGETupdated";
|
||||
testType = targetTypeManagement.update(entityFactory.targetType().update(testType.getId()).name(typeNameUpdated)
|
||||
.description("Updated Description").colour("#ffffff"));
|
||||
|
||||
mvc.perform(get(TARGETTYPE_SINGLE_ENDPOINT, testType.getId()).accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
|
||||
.andExpect(jsonPath("$.name", equalTo(typeNameUpdated)))
|
||||
.andExpect(jsonPath("$.description", equalTo("Updated Description")))
|
||||
.andExpect(jsonPath("$.colour", equalTo("#ffffff")))
|
||||
.andExpect(jsonPath("$.createdBy", equalTo(TEST_USER)))
|
||||
.andExpect(jsonPath("$.createdAt", equalTo(testType.getCreatedAt())))
|
||||
.andExpect(jsonPath("$.lastModifiedBy", equalTo(TEST_USER)))
|
||||
.andExpect(jsonPath("$.lastModifiedAt", equalTo(testType.getLastModifiedAt())))
|
||||
.andExpect(jsonPath("$.deleted").doesNotExist())
|
||||
.andExpect(jsonPath("$.key").doesNotExist());
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithUser(principal = TEST_USER, allSpPermissions = true)
|
||||
@Description("Checks the correct behaviour of /rest/v1/targettypes POST requests.")
|
||||
void createTargetTypes() throws Exception {
|
||||
String typeName = "TestTypePOST";
|
||||
final List<TargetType> types = buildTestTargetTypesWithoutDsTypes(typeName, 5);
|
||||
|
||||
runPostTargetTypeAndVerify(types);
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithUser(principal = TEST_USER, allSpPermissions = true)
|
||||
@Description("Checks the correct behaviour of /rest/v1/targettypes/{ID}/compatibledistributionsettypes POST requests.")
|
||||
void addDistributionSetTypeToTargetType() throws Exception {
|
||||
String typeName = "TestTypeAddDs";
|
||||
TargetType testType = createTestTargetTypeInDB(typeName);
|
||||
assertThat(testType.getOptLockRevision()).isEqualTo(1);
|
||||
|
||||
mvc.perform(post(TARGETTYPE_DSTYPES_ENDPOINT, testType.getId())
|
||||
.content("[{\"id\":" + standardDsType.getId() + "}]").contentType(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
|
||||
|
||||
testType = targetTypeManagement.get(testType.getId()).get();
|
||||
assertThat(testType.getLastModifiedBy()).isEqualTo(TEST_USER);
|
||||
assertThat(testType.getOptLockRevision()).isEqualTo(2);
|
||||
assertThat(testType.getCompatibleDistributionSetTypes()).containsExactly(standardDsType);
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithUser(principal = TEST_USER, allSpPermissions = true)
|
||||
@Description("Checks the correct behaviour of /rest/v1/targettypes/{ID}/compatibledistributionsettypes GET requests.")
|
||||
void getDistributionSetsOfTargetType() throws Exception {
|
||||
String typeName = "TestTypeGetDs";
|
||||
final TargetType testType = createTestTargetTypeInDB(typeName, Collections.singletonList(standardDsType));
|
||||
|
||||
mvc.perform(get(TARGETTYPE_DSTYPES_ENDPOINT, testType.getId()).accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
|
||||
.andExpect(jsonPath("$[0].name", equalTo(standardDsType.getName())))
|
||||
.andExpect(jsonPath("$[0].description", equalTo(standardDsType.getDescription())))
|
||||
.andExpect(jsonPath("$[0].key", equalTo("test_default_ds_type")))
|
||||
.andExpect(jsonPath("$[0]._links.self.href",
|
||||
equalTo("http://localhost/rest/v1/distributionsettypes/" + standardDsType.getId())));
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithUser(principal = TEST_USER, allSpPermissions = true)
|
||||
@Description("Checks the correct behaviour of /rest/v1/targettypes/{ID}/compatibledistributionsettypes/{ID} GET requests.")
|
||||
void getDistributionSetOfTargetTypeReturnsNotAllowed() throws Exception {
|
||||
String typeName = "TestTypeAddDs";
|
||||
final TargetType testType = createTestTargetTypeInDB(typeName);
|
||||
|
||||
mvc.perform(get(TARGETTYPE_DSTYPE_SINGLE_ENDPOINT, testType.getId(), standardDsType.getId())
|
||||
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isMethodNotAllowed());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithUser(principal = TEST_USER, allSpPermissions = true)
|
||||
@Description("Checks the correct behaviour of /rest/v1/targettypes/{ID}/compatibledistributionsettypes/{ID} DELETE requests.")
|
||||
void removeDsTypeFromTargetType() throws Exception {
|
||||
String typeName = "TestTypeRemoveDs";
|
||||
TargetType testType = createTestTargetTypeInDB(typeName, Collections.singletonList(standardDsType));
|
||||
|
||||
mvc.perform(delete(TARGETTYPE_DSTYPE_SINGLE_ENDPOINT, testType.getId(), standardDsType.getId())
|
||||
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isOk());
|
||||
|
||||
testType = targetTypeManagement.get(testType.getId()).get();
|
||||
assertThat(testType.getLastModifiedBy()).isEqualTo(TEST_USER);
|
||||
assertThat(testType.getOptLockRevision()).isEqualTo(2);
|
||||
assertThat(testType.getCompatibleDistributionSetTypes()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithUser(principal = TEST_USER, allSpPermissions = true)
|
||||
@Description("Checks the correct behaviour of /rest/v1/distributionsettypes/{ID} DELETE requests.")
|
||||
void deletingDsTypeRemovesAssignmentFromTargetType() throws Exception {
|
||||
String typeName = "TestTypeRemoveDs";
|
||||
TargetType testType = createTestTargetTypeInDB(typeName, Collections.singletonList(standardDsType));
|
||||
assertThat(testType.getCompatibleDistributionSetTypes()).hasSize(1);
|
||||
assertThat(distributionSetTypeManagement.getByKey(standardDsType.getKey())).isNotEmpty();
|
||||
|
||||
mvc.perform(delete(MgmtRestConstants.DISTRIBUTIONSETTYPE_V1_REQUEST_MAPPING + "/" + standardDsType.getId()))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
|
||||
|
||||
testType = targetTypeManagement.get(testType.getId()).get();
|
||||
assertThat(testType.getLastModifiedBy()).isEqualTo(TEST_USER);
|
||||
assertThat(testType.getOptLockRevision()).isEqualTo(2);
|
||||
assertThat(testType.getCompatibleDistributionSetTypes()).isEmpty();
|
||||
assertThat(distributionSetTypeManagement.getByKey(standardDsType.getKey())).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithUser(principal = TEST_USER, allSpPermissions = true)
|
||||
@Description("Checks the correct behaviour of /rest/v1/targettypes/{ID} DELETE requests - Deletion when not in use.")
|
||||
void deleteTargetTypeUnused() throws Exception {
|
||||
String typeName = "TestTypeUnusedDelete";
|
||||
final TargetType testType = createTestTargetTypeInDB(typeName);
|
||||
|
||||
assertThat(targetTypeManagement.count()).isEqualTo(1);
|
||||
|
||||
mvc.perform(delete(TARGETTYPE_SINGLE_ENDPOINT, testType.getId())).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isOk());
|
||||
|
||||
assertThat(targetTypeManagement.count()).isZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithUser(principal = TEST_USER, allSpPermissions = true)
|
||||
@Description("Checks the correct behaviour of /rest/v1/targettypes/{ID} DELETE requests - Deletion not possible when in use.")
|
||||
void deleteTargetTypeUsed() throws Exception {
|
||||
String typeName = "TestTypeUsedDelete";
|
||||
final TargetType testType = createTestTargetTypeInDB(typeName);
|
||||
|
||||
targetManagement.create(entityFactory.target().create().controllerId("target").name("TargetOfTestType")
|
||||
.description("target description").targetType(testType.getId()));
|
||||
|
||||
assertThat(targetTypeManagement.count()).isEqualTo(1);
|
||||
assertThat(targetManagement.count()).isEqualTo(1);
|
||||
|
||||
mvc.perform(delete(TARGETTYPE_SINGLE_ENDPOINT, testType.getId())).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isConflict());
|
||||
|
||||
assertThat(targetManagement.count()).isEqualTo(1);
|
||||
assertThat(targetTypeManagement.count()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithUser(principal = TEST_USER, allSpPermissions = true)
|
||||
@Description("Ensures that target type deletion request to API on an entity that does not exist results in NOT_FOUND.")
|
||||
void deleteTargetTypeThatDoesNotExistLeadsToNotFound() throws Exception {
|
||||
mvc.perform(delete(TARGETTYPE_SINGLE_ENDPOINT, 1234)).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isNotFound());
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithUser(principal = TEST_USER, allSpPermissions = true)
|
||||
@Description("Tests the update of the deletion flag. It is verified that the target type can't be marked as deleted through update operation.")
|
||||
void updateTargetTypeDeletedFlag() throws Exception {
|
||||
String typeName = "TestTypePUT";
|
||||
final TargetType testType = createTestTargetTypeInDB(typeName);
|
||||
|
||||
final String body = new JSONObject().put("id", testType.getId()).put("deleted", true).toString();
|
||||
|
||||
mvc.perform(
|
||||
put(TARGETTYPE_SINGLE_ENDPOINT, testType.getId()).content(body).contentType(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.id", equalTo(testType.getId().intValue())))
|
||||
.andExpect(jsonPath("$.deleted").doesNotExist());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Ensures that the server is behaving as expected on invalid requests (wrong media type, wrong ID etc.).")
|
||||
void invalidRequestsOnTargetTypesResource() throws Exception {
|
||||
String typeName = "TestTypeInvalidReq";
|
||||
final TargetType testType = createTestTargetTypeInDB(typeName, Collections.singletonList(standardDsType));
|
||||
|
||||
// target type does not exist
|
||||
mvc.perform(get(TARGETTYPE_SINGLE_ENDPOINT, 12345678)).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isNotFound());
|
||||
mvc.perform(get(TARGETTYPE_DSTYPES_ENDPOINT, 123456789)).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isNotFound());
|
||||
mvc.perform(delete(TARGETTYPE_SINGLE_ENDPOINT, 123456789)).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
// target types at creation time invalid
|
||||
final TargetType testNewType = createTestTargetTypeInDB(typeName + "Another",
|
||||
Collections.singletonList(standardDsType));
|
||||
|
||||
mvc.perform(post(TARGETTYPES_ENDPOINT).content(JsonBuilder.targetTypes(Collections.singletonList(testNewType)))
|
||||
.contentType(MediaType.APPLICATION_OCTET_STREAM)).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isUnsupportedMediaType());
|
||||
|
||||
// bad request - no content
|
||||
mvc.perform(post(TARGETTYPES_ENDPOINT).contentType(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest());
|
||||
|
||||
// bad request - bad content
|
||||
mvc.perform(post(TARGETTYPES_ENDPOINT).content("sdfjsdlkjfskdjf".getBytes())
|
||||
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isBadRequest());
|
||||
|
||||
// Missing mandatory field name
|
||||
mvc.perform(post(TARGETTYPES_ENDPOINT).content("[{\"description\":\"Desc123\"}]")
|
||||
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isBadRequest());
|
||||
|
||||
final TargetType tooLongName = entityFactory.targetType().create()
|
||||
.name(RandomStringUtils.randomAlphanumeric(NamedEntity.NAME_MAX_SIZE + 1)).build();
|
||||
mvc.perform(post(TARGETTYPES_ENDPOINT).content(JsonBuilder.targetTypes(Collections.singletonList(tooLongName)))
|
||||
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isBadRequest());
|
||||
|
||||
// ds types
|
||||
mvc.perform(get(TARGETTYPE_DSTYPES_ENDPOINT, testType.getId())).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isOk());
|
||||
|
||||
mvc.perform(delete(TARGETTYPE_DSTYPE_SINGLE_ENDPOINT, testType.getId(), 565765))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
|
||||
|
||||
mvc.perform(post(TARGETTYPE_DSTYPES_ENDPOINT, testType.getId()).content("{\"id\":1}")
|
||||
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isBadRequest());
|
||||
|
||||
mvc.perform(post(TARGETTYPE_DSTYPES_ENDPOINT, testType.getId()).content("[{\"id\":44456}]")
|
||||
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
// not allowed methods
|
||||
mvc.perform(put(TARGETTYPES_ENDPOINT)).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isMethodNotAllowed());
|
||||
|
||||
mvc.perform(delete(TARGETTYPES_ENDPOINT)).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isMethodNotAllowed());
|
||||
|
||||
mvc.perform(put(TARGETTYPE_DSTYPES_ENDPOINT, testType.getId())).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isMethodNotAllowed());
|
||||
|
||||
mvc.perform(delete(TARGETTYPE_DSTYPES_ENDPOINT, testType.getId(), 565765)).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isMethodNotAllowed());
|
||||
|
||||
mvc.perform(put(TARGETTYPE_DSTYPE_SINGLE_ENDPOINT, testType.getId(), 565765))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isMethodNotAllowed());
|
||||
|
||||
mvc.perform(post(TARGETTYPE_DSTYPE_SINGLE_ENDPOINT, testType.getId(), 565765)
|
||||
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isMethodNotAllowed());
|
||||
|
||||
mvc.perform(get(TARGETTYPE_DSTYPE_SINGLE_ENDPOINT, testType.getId(), 565765))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isMethodNotAllowed());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Search request of target types.")
|
||||
void searchTargetTypeRsql() throws Exception {
|
||||
targetTypeManagement.create(entityFactory.targetType().create().name("TestName123"));
|
||||
targetTypeManagement.create(entityFactory.targetType().create().name("TestName1234"));
|
||||
|
||||
final String rsqlFindLikeDs1OrDs2 = "name==TestName123,name==TestName1234";
|
||||
|
||||
mvc.perform(get(TARGETTYPES_ENDPOINT + "?q=" + rsqlFindLikeDs1OrDs2)).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isOk()).andExpect(jsonPath("size", equalTo(2)))
|
||||
.andExpect(jsonPath("total", equalTo(2))).andExpect(jsonPath("content[0].name", equalTo("TestName123")))
|
||||
.andExpect(jsonPath("content[1].name", equalTo("TestName1234")));
|
||||
}
|
||||
|
||||
@Step
|
||||
private TargetType buildTestTargetTypeBody(String name) {
|
||||
return prepareTestTargetType(name, null).build();
|
||||
}
|
||||
|
||||
private TargetTypeCreate prepareTestTargetType(String name, Collection<DistributionSetType> dsTypes) {
|
||||
TargetTypeCreate create = entityFactory.targetType().create().name(name)
|
||||
.description("Description of the test type").colour("#aaaaaa");
|
||||
if (dsTypes != null && !dsTypes.isEmpty()) {
|
||||
create.compatible(Collections.singletonList(standardDsType.getId()));
|
||||
}
|
||||
return create;
|
||||
}
|
||||
|
||||
@Step
|
||||
private List<TargetType> createTestTargetTypesInDB(String namePrefix, int count) {
|
||||
return testdataFactory.createTargetTypes(namePrefix, count);
|
||||
}
|
||||
|
||||
@Step
|
||||
private TargetType createTestTargetTypeInDB(String name) {
|
||||
return testdataFactory.findOrCreateTargetType(name);
|
||||
}
|
||||
|
||||
@Step
|
||||
private TargetType createTestTargetTypeInDB(String name, List<DistributionSetType> dsTypes) {
|
||||
TargetType targetType = testdataFactory.createTargetType(name, dsTypes);
|
||||
assertThat(targetType.getOptLockRevision()).isEqualTo(1);
|
||||
return targetType;
|
||||
}
|
||||
|
||||
@Step
|
||||
private List<TargetType> buildTestTargetTypesWithoutDsTypes(String namePrefix, int count) {
|
||||
final List<TargetType> types = new ArrayList<>();
|
||||
for (int index = 0; index < count; index++) {
|
||||
types.add(buildTestTargetTypeBody(namePrefix + index));
|
||||
}
|
||||
return types;
|
||||
}
|
||||
|
||||
@Step
|
||||
private void runPostTargetTypeAndVerify(final List<TargetType> types) throws Exception {
|
||||
int size = types.size();
|
||||
ResultActions resultActions = mvc
|
||||
.perform(post(TARGETTYPES_ENDPOINT).content(JsonBuilder.targetTypes(types))
|
||||
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print());
|
||||
|
||||
for (int index = 0; index < size; index++) {
|
||||
resultActions.andExpect(status().isCreated()).andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
|
||||
.andExpect(jsonPath("$[" + index + "].id").exists())
|
||||
.andExpect(jsonPath("$[" + index + "].name", startsWith("TestTypePOST")))
|
||||
.andExpect(jsonPath("$[" + index + "].colour", hasToString("#aaaaaa")))
|
||||
.andExpect(jsonPath("$[" + index + "].description",
|
||||
equalTo("Description of the test type")))
|
||||
.andExpect(jsonPath("$[" + index + "].createdBy", equalTo(TEST_USER)))
|
||||
.andExpect(jsonPath("$[" + index + "].createdAt").exists())
|
||||
.andExpect(jsonPath("$[" + index + "].lastModifiedBy", equalTo(TEST_USER)))
|
||||
.andExpect(jsonPath("$[" + index + "].lastModifiedAt").exists())
|
||||
.andExpect(jsonPath("$[" + index + "].key").doesNotExist())
|
||||
.andExpect(jsonPath("$[" + index + "].deleted").doesNotExist())
|
||||
.andExpect(jsonPath("$[" + index + "]._links.self.href",
|
||||
startsWith("http://localhost/rest/v1/targettypes/")))
|
||||
.andExpect(
|
||||
jsonPath("$[" + index + "]._links.compatibledistributionsettypes.href")
|
||||
.doesNotExist());
|
||||
}
|
||||
MvcResult mvcResult = resultActions.andReturn();
|
||||
|
||||
for (int index = 0; index < size; index++) {
|
||||
String name = "TestTypePOST" + index;
|
||||
final TargetType created = targetTypeManagement.getByName(name).get();
|
||||
|
||||
assertThat(JsonPath.compile("$[ ?(@.name=='" + name + "') ].id")
|
||||
.read(mvcResult.getResponse().getContentAsString()).toString()).contains(String.valueOf(created.getId()));
|
||||
assertThat(JsonPath.compile("$[ ?(@.name=='" + name + "') ]._links.self.href")
|
||||
.read(mvcResult.getResponse().getContentAsString()).toString()).contains("/"+created.getId());
|
||||
}
|
||||
|
||||
assertThat(targetTypeManagement.count()).isEqualTo(size);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -87,6 +87,7 @@ public class ResponseExceptionHandler {
|
||||
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_CONFIGURATION_VALUE_CHANGE_NOT_ALLOWED, HttpStatus.FORBIDDEN);
|
||||
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_MULTIASSIGNMENT_NOT_ENABLED, HttpStatus.BAD_REQUEST);
|
||||
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_NO_WEIGHT_PROVIDED_IN_MULTIASSIGNMENT_MODE, HttpStatus.BAD_REQUEST);
|
||||
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_TARGET_TYPE_IN_USE, HttpStatus.CONFLICT);
|
||||
}
|
||||
|
||||
private static HttpStatus getStatusOrDefault(final SpServerError error) {
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||
import org.eclipse.hawkbit.repository.model.Tag;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetType;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
@@ -426,13 +427,13 @@ public abstract class JsonBuilder {
|
||||
int i = 0;
|
||||
for (final Target target : targets) {
|
||||
final String address = target.getAddress() != null ? target.getAddress().toString() : null;
|
||||
|
||||
final String targetType = target.getTargetType() != null ? target.getTargetType().getId().toString() : null;
|
||||
final String token = withToken ? target.getSecurityToken() : null;
|
||||
|
||||
builder.append(new JSONObject().put("controllerId", target.getControllerId())
|
||||
.put("description", target.getDescription()).put("name", target.getName()).put("createdAt", "0")
|
||||
.put("updatedAt", "0").put("createdBy", "fghdfkjghdfkjh").put("updatedBy", "fghdfkjghdfkjh")
|
||||
.put("address", address).put("securityToken", token).toString());
|
||||
.put("updatedAt", "0").put("createdBy", "systemtest").put("updatedBy", "systemtest")
|
||||
.put("address", address).put("securityToken", token).put("targetType", targetType).toString());
|
||||
|
||||
if (++i < targets.size()) {
|
||||
builder.append(",");
|
||||
@@ -444,6 +445,83 @@ public abstract class JsonBuilder {
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public static String targets(final List<Target> targets, final boolean withToken, final long targetTypeId) throws JSONException {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.append("[");
|
||||
int i = 0;
|
||||
for (final Target target : targets) {
|
||||
final String address = target.getAddress() != null ? target.getAddress().toString() : null;
|
||||
final String type = target.getTargetType() != null ? target.getTargetType().getId().toString() : null;
|
||||
final String token = withToken ? target.getSecurityToken() : null;
|
||||
|
||||
builder.append(new JSONObject().put("controllerId", target.getControllerId())
|
||||
.put("description", target.getDescription()).put("name", target.getName()).put("createdAt", "0")
|
||||
.put("updatedAt", "0").put("createdBy", "fghdfkjghdfkjh").put("updatedBy", "fghdfkjghdfkjh")
|
||||
.put("address", address).put("securityToken", token).put("targetType", targetTypeId).toString());
|
||||
|
||||
if (++i < targets.size()) {
|
||||
builder.append(",");
|
||||
}
|
||||
}
|
||||
|
||||
builder.append("]");
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public static String targetTypes(final List<TargetType> types) throws JSONException {
|
||||
final JSONArray result = new JSONArray();
|
||||
|
||||
for (final TargetType type : types) {
|
||||
|
||||
final JSONArray dsTypes = new JSONArray();
|
||||
type.getCompatibleDistributionSetTypes().forEach(dsType -> {
|
||||
try {
|
||||
dsTypes.put(new JSONObject().put("id", dsType.getId()));
|
||||
} catch (final JSONException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
});
|
||||
|
||||
result.put(new JSONObject().put("name", type.getName()).put("description", type.getDescription())
|
||||
.put("id", Long.MAX_VALUE).put("colour", type.getColour()).put("createdAt", "0").put("updatedAt", "0")
|
||||
.put("createdBy", "fghdfkjghdfkjh").put("updatedBy", "fghdfkjghdfkjh")
|
||||
.put("distributionsets", dsTypes));
|
||||
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
public static String targetTypesCreatableFieldsOnly(final List<TargetType> types) throws JSONException {
|
||||
final JSONArray result = new JSONArray();
|
||||
|
||||
for (final TargetType type : types) {
|
||||
|
||||
final JSONArray dsTypes = new JSONArray();
|
||||
type.getCompatibleDistributionSetTypes().forEach(dsType -> {
|
||||
try {
|
||||
dsTypes.put(new JSONObject().put("id", dsType.getId()));
|
||||
} catch (final JSONException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
});
|
||||
|
||||
JSONObject json = new JSONObject().put("name", type.getName()).put("description", type.getDescription())
|
||||
.put("colour", type.getColour());
|
||||
|
||||
if(dsTypes.length() != 0)
|
||||
{
|
||||
json.put("compatibledistributionsettypes", dsTypes);
|
||||
}
|
||||
|
||||
result.put(json);
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
public static String rollout(final String name, final String description, final int groupSize,
|
||||
final long distributionSetId, final String targetFilterQuery, final RolloutGroupConditions conditions) {
|
||||
return rollout(name, description, groupSize, distributionSetId, targetFilterQuery, conditions, null, null,
|
||||
|
||||
@@ -0,0 +1,396 @@
|
||||
:doctype: book
|
||||
:icons: font
|
||||
:source-highlighter: highlightjs
|
||||
:toc: macro
|
||||
:toclevels: 1
|
||||
:sectlinks:
|
||||
:linkattrs:
|
||||
|
||||
[[target-types]]
|
||||
= Target Types
|
||||
|
||||
toc::[]
|
||||
|
||||
|
||||
== GET /rest/v1/targettypes
|
||||
|
||||
=== Implementation notes
|
||||
|
||||
Handles the GET request of retrieving all target types within Hawkbit. Required Permission: READ_TARGET
|
||||
|
||||
=== Get target types
|
||||
|
||||
==== CURL
|
||||
|
||||
include::{snippets}/targettypes/get-target-types/curl-request.adoc[]
|
||||
|
||||
==== Request URL
|
||||
|
||||
include::{snippets}/targettypes/get-target-types/http-request.adoc[]
|
||||
|
||||
==== Request query parameter
|
||||
|
||||
include::{snippets}/targettypes/get-target-types-with-parameters/request-parameters.adoc[]
|
||||
|
||||
==== Request parameter example
|
||||
|
||||
include::{snippets}/targettypes/get-target-types-with-parameters/http-request.adoc[]
|
||||
|
||||
=== Response (Status 200)
|
||||
|
||||
==== Response fields
|
||||
|
||||
include::{snippets}/targettypes/get-target-types/response-fields.adoc[]
|
||||
|
||||
==== Response example
|
||||
|
||||
include::{snippets}/targettypes/get-target-types/http-response.adoc[]
|
||||
|
||||
=== Error responses
|
||||
|
||||
|===
|
||||
| HTTP Status Code | Reason | Response Model
|
||||
|
||||
include::../errors/400.adoc[]
|
||||
include::../errors/401.adoc[]
|
||||
include::../errors/403.adoc[]
|
||||
include::../errors/405.adoc[]
|
||||
include::../errors/406.adoc[]
|
||||
include::../errors/429.adoc[]
|
||||
|===
|
||||
|
||||
|
||||
== POST /rest/v1/targettypes
|
||||
|
||||
=== Implementation notes
|
||||
|
||||
Handles the POST request for creating new target types within Hawkbit. The request body must always be a list of types. Required Permission: CREATE_TARGET
|
||||
|
||||
=== Create target types
|
||||
|
||||
==== CURL
|
||||
|
||||
include::{snippets}/targettypes/post-target-types/curl-request.adoc[]
|
||||
|
||||
==== Request URL
|
||||
|
||||
include::{snippets}/targettypes/post-target-types/http-request.adoc[]
|
||||
|
||||
==== Request fields
|
||||
|
||||
include::{snippets}/targettypes/post-target-types/request-fields.adoc[]
|
||||
|
||||
=== Response (Status 201)
|
||||
|
||||
==== Response fields
|
||||
|
||||
include::{snippets}/targettypes/post-target-types/response-fields.adoc[]
|
||||
|
||||
==== Response example
|
||||
|
||||
include::{snippets}/targettypes/post-target-types/http-response.adoc[]
|
||||
|
||||
=== Error responses
|
||||
|
||||
|===
|
||||
| HTTP Status Code | Reason | Response Model
|
||||
|
||||
include::../errors/400.adoc[]
|
||||
include::../errors/401.adoc[]
|
||||
include::../errors/403.adoc[]
|
||||
| `404 Not Found`
|
||||
| Target type was not found.
|
||||
| See <<error-body>>
|
||||
include::../errors/405.adoc[]
|
||||
include::../errors/406.adoc[]
|
||||
| `409 Conflict`
|
||||
| Target type already exists
|
||||
| See <<error-body>>
|
||||
include::../errors/415.adoc[]
|
||||
include::../errors/429.adoc[]
|
||||
|===
|
||||
|
||||
|
||||
== DELETE /rest/v1/targettypes/{targetTypeId}
|
||||
|
||||
=== Implementation Notes
|
||||
|
||||
Handles the DELETE request for a single target type within Hawkbit. Required Permission: DELETE_TARGET
|
||||
|
||||
=== Delete target type
|
||||
|
||||
==== CURL
|
||||
|
||||
include::{snippets}/targettypes/delete-target-type/curl-request.adoc[]
|
||||
|
||||
==== Request URL
|
||||
|
||||
include::{snippets}/targettypes/delete-target-type/http-request.adoc[]
|
||||
|
||||
==== Request path parameter
|
||||
|
||||
include::{snippets}/targettypes/delete-target-type/path-parameters.adoc[]
|
||||
|
||||
=== Response (Status 200)
|
||||
|
||||
==== Response example
|
||||
|
||||
include::{snippets}/targettypes/delete-target-type/http-response.adoc[]
|
||||
|
||||
=== Error responses
|
||||
|
||||
|===
|
||||
| HTTP Status Code | Reason | Response Model
|
||||
|
||||
include::../errors/400.adoc[]
|
||||
include::../errors/401.adoc[]
|
||||
include::../errors/403.adoc[]
|
||||
| `404 Not Found`
|
||||
| Target type was not found.
|
||||
| See <<error-body>>
|
||||
include::../errors/405.adoc[]
|
||||
include::../errors/406.adoc[]
|
||||
include::../errors/429.adoc[]
|
||||
|===
|
||||
|
||||
|
||||
== GET /rest/v1/targettypes/{targetTypeId}
|
||||
|
||||
=== Implementation notes
|
||||
|
||||
Handles the GET request of retrieving a single target type within Hawkbit. Required Permission: READ_TARGET
|
||||
|
||||
=== Get target type
|
||||
|
||||
==== CURL
|
||||
|
||||
include::{snippets}/targettypes/get-target-type/curl-request.adoc[]
|
||||
|
||||
==== Request URL
|
||||
|
||||
include::{snippets}/targettypes/get-target-type/http-request.adoc[]
|
||||
|
||||
==== Request path parameter
|
||||
|
||||
include::{snippets}/targettypes/get-target-type/path-parameters.adoc[]
|
||||
|
||||
=== Response (Status 200)
|
||||
|
||||
==== Response fields
|
||||
|
||||
include::{snippets}/targettypes/get-target-type/response-fields.adoc[]
|
||||
|
||||
==== Response example
|
||||
|
||||
include::{snippets}/targettypes/get-target-type/http-response.adoc[]
|
||||
|
||||
=== Error responses
|
||||
|
||||
|===
|
||||
| HTTP Status Code | Reason | Response Model
|
||||
|
||||
include::../errors/400.adoc[]
|
||||
include::../errors/401.adoc[]
|
||||
include::../errors/403.adoc[]
|
||||
| `404 Not Found`
|
||||
| Target type was not found.
|
||||
| See <<error-body>>
|
||||
include::../errors/405.adoc[]
|
||||
include::../errors/406.adoc[]
|
||||
include::../errors/429.adoc[]
|
||||
|===
|
||||
|
||||
|
||||
== PUT /rest/v1/targettypes/{targetTypeId}
|
||||
|
||||
=== Implementation notes
|
||||
|
||||
Handles the PUT request for a single target type within Hawkbit. Required Permission: UPDATE_TARGET
|
||||
|
||||
=== Update target type
|
||||
|
||||
==== CURL
|
||||
|
||||
include::{snippets}/targettypes/put-target-type/curl-request.adoc[]
|
||||
|
||||
==== Request URL
|
||||
|
||||
include::{snippets}/targettypes/put-target-type/http-request.adoc[]
|
||||
|
||||
==== Request path parameter
|
||||
|
||||
include::{snippets}/targettypes/put-target-type/path-parameters.adoc[]
|
||||
|
||||
==== Request fields
|
||||
|
||||
include::{snippets}/targettypes/put-target-type/request-fields.adoc[]
|
||||
|
||||
=== Response (Status 200)
|
||||
|
||||
==== Response fields
|
||||
|
||||
include::{snippets}/targettypes/put-target-type/response-fields.adoc[]
|
||||
|
||||
==== Response example
|
||||
|
||||
include::{snippets}/targettypes/put-target-type/http-response.adoc[]
|
||||
|
||||
=== Error responses
|
||||
|
||||
|===
|
||||
| HTTP Status Code | Reason | Response Model
|
||||
|
||||
include::../errors/400.adoc[]
|
||||
include::../errors/401.adoc[]
|
||||
include::../errors/403.adoc[]
|
||||
| `404 Not Found`
|
||||
| Target type was not found.
|
||||
| See <<error-body>>
|
||||
include::../errors/405.adoc[]
|
||||
include::../errors/406.adoc[]
|
||||
include::../errors/409.adoc[]
|
||||
include::../errors/415.adoc[]
|
||||
include::../errors/429.adoc[]
|
||||
|===
|
||||
|
||||
|
||||
== GET /rest/v1/targettypes/{targetTypeId}/compatibledistributionsettypes
|
||||
|
||||
=== Implementation notes
|
||||
|
||||
Handles the GET request of retrieving the list of compatible distribution set types in that target type. Required Permission: READ_TARGET, READ_REPOSITORY
|
||||
|
||||
=== Lists all compatible distribution set types
|
||||
|
||||
==== CURL
|
||||
|
||||
include::{snippets}/targettypes/get-compatible-distribution-set-types/curl-request.adoc[]
|
||||
|
||||
==== Request URL
|
||||
|
||||
include::{snippets}/targettypes/get-compatible-distribution-set-types/http-request.adoc[]
|
||||
|
||||
==== Request path parameter
|
||||
|
||||
include::{snippets}/targettypes/get-compatible-distribution-set-types/path-parameters.adoc[]
|
||||
|
||||
=== Response (Status 200)
|
||||
|
||||
==== Response fields
|
||||
|
||||
include::{snippets}/targettypes/get-compatible-distribution-set-types/response-fields.adoc[]
|
||||
|
||||
==== Response example
|
||||
|
||||
include::{snippets}/targettypes/get-compatible-distribution-set-types/http-response.adoc[]
|
||||
|
||||
=== Error responses
|
||||
|
||||
|===
|
||||
| HTTP Status Code | Reason | Response Model
|
||||
|
||||
include::../errors/400.adoc[]
|
||||
include::../errors/401.adoc[]
|
||||
include::../errors/403.adoc[]
|
||||
| `404 Not Found`
|
||||
| Distribution set type was not found.
|
||||
| See <<error-body>>
|
||||
include::../errors/405.adoc[]
|
||||
include::../errors/406.adoc[]
|
||||
include::../errors/429.adoc[]
|
||||
|===
|
||||
|
||||
|
||||
== POST /rest/v1/targettypes/{targetTypeId}/compatibledistributionsettypes
|
||||
|
||||
=== Implementation notes
|
||||
|
||||
Handles the POST request for adding compatible distribution set types to a target type. Required Permission: UPDATE_TARGET and READ_REPOSITORY
|
||||
|
||||
=== Add compatible distribution set type
|
||||
|
||||
==== CURL
|
||||
|
||||
include::{snippets}/targettypes/post-compatible-distribution-set-types/curl-request.adoc[]
|
||||
|
||||
==== Request URL
|
||||
|
||||
include::{snippets}/targettypes/post-compatible-distribution-set-types/http-request.adoc[]
|
||||
|
||||
==== Request path parameter
|
||||
|
||||
include::{snippets}/targettypes/post-compatible-distribution-set-types/path-parameters.adoc[]
|
||||
|
||||
==== Request fields
|
||||
|
||||
include::{snippets}/targettypes/post-compatible-distribution-set-types/request-fields.adoc[]
|
||||
|
||||
=== Response (Status 201)
|
||||
|
||||
==== Response example
|
||||
|
||||
include::{snippets}/targettypes/post-compatible-distribution-set-types/http-response.adoc[]
|
||||
|
||||
=== Error responses
|
||||
|
||||
|===
|
||||
| HTTP Status Code | Reason | Response Model
|
||||
|
||||
include::../errors/400.adoc[]
|
||||
include::../errors/401.adoc[]
|
||||
include::../errors/403.adoc[]
|
||||
| `404 Not Found`
|
||||
| Distribution set type was not found.
|
||||
| See <<error-body>>
|
||||
include::../errors/405.adoc[]
|
||||
include::../errors/406.adoc[]
|
||||
| `409 Conflict`
|
||||
| Distribution set type already exists
|
||||
| See <<error-body>>
|
||||
include::../errors/415.adoc[]
|
||||
include::../errors/429.adoc[]
|
||||
|===
|
||||
|
||||
|
||||
== DELETE /rest/v1/targettypes/{targetTypeId}/compatibledistributionsettypes/{distributionSetTypeId}
|
||||
|
||||
=== Implementation Notes
|
||||
|
||||
Handles the DELETE request for removing a distribution set type from a single target type. Required Permission: UPDATE_TARGET and READ_REPOSITORY
|
||||
|
||||
=== Remove compatible distribution set type from target type
|
||||
|
||||
==== CURL
|
||||
|
||||
include::{snippets}/targettypes/delete-compatible-distribution-set-type/curl-request.adoc[]
|
||||
|
||||
==== Request URL
|
||||
|
||||
include::{snippets}/targettypes/delete-compatible-distribution-set-type/http-request.adoc[]
|
||||
|
||||
==== Request path parameter
|
||||
|
||||
include::{snippets}/targettypes/delete-compatible-distribution-set-type/path-parameters.adoc[]
|
||||
|
||||
=== Response (Status 200)
|
||||
|
||||
==== Response example
|
||||
|
||||
include::{snippets}/targettypes/delete-compatible-distribution-set-type/http-response.adoc[]
|
||||
|
||||
=== Error responses
|
||||
|
||||
|===
|
||||
| HTTP Status Code | Reason | Response Model
|
||||
|
||||
include::../errors/400.adoc[]
|
||||
include::../errors/401.adoc[]
|
||||
include::../errors/403.adoc[]
|
||||
| `404 Not Found`
|
||||
| Distribution set type was not found.
|
||||
| See <<error-body>>
|
||||
include::../errors/405.adoc[]
|
||||
include::../errors/406.adoc[]
|
||||
include::../errors/429.adoc[]
|
||||
|===
|
||||
|
||||
@@ -19,12 +19,12 @@ public final class ApiModelPropertiesGeneric {
|
||||
public static final String ITEM_ID = "The technical identifier " + ENDING;
|
||||
public static final String NAME = "The name" + ENDING;
|
||||
public static final String DESCRPTION = "The description" + ENDING;
|
||||
public static final String COLOUR = "The colour" + ENDING;
|
||||
public static final String COLOUR = "The colour" + ENDING + ". In HEX format, e.g. #800080";
|
||||
public static final String DELETED = "Deleted flag, used for soft deleted entities";
|
||||
|
||||
public static final String CREATED_BY = "Entity was originally created by User, AMQP-Controller, anonymous etc.)";
|
||||
public static final String CREATED_BY = "Entity was originally created by (User, AMQP-Controller, anonymous etc.)";
|
||||
public static final String CREATED_AT = "Entity was originally created at (timestamp UTC in milliseconds)";
|
||||
public static final String LAST_MODIFIED_BY = "Entity was last modified by User, AMQP-Controller, anonymous etc.)";
|
||||
public static final String LAST_MODIFIED_BY = "Entity was last modified by (User, AMQP-Controller, anonymous etc.)";
|
||||
public static final String LAST_MODIFIED_AT = "Entity was last modified at (timestamp UTC in milliseconds)";
|
||||
|
||||
// Paging elements
|
||||
|
||||
@@ -78,6 +78,10 @@ public final class MgmtApiModelProperties {
|
||||
public static final String POLL_STATUS = "Poll status of the target. In many scenarios that target will poll the update server on a regular basis to look for potential updates. If that poll does not happen it might imply that the target is offline.";
|
||||
public static final String POLL_OVERDUE = "Defines if the target poll time is overdue based on the next expected poll time plus the configured overdue poll time threshold.";
|
||||
|
||||
// Target type
|
||||
public static final String COMPATIBLE_DS_TYPES = "Array of distribution set types that are compatible to that target type";
|
||||
public static final String LINK_COMPATIBLE_DS_TYPES = "Link to the compatible distribution set types in this target type";
|
||||
|
||||
// rollout
|
||||
public static final String ROLLOUT_FILTER_QUERY = "target filter query language expression";
|
||||
public static final String ROLLOUT_GROUP_FILTER_QUERY = "target filter query language expression that selects a subset of targets which match the target filter of the Rollout";
|
||||
@@ -118,6 +122,8 @@ public final class MgmtApiModelProperties {
|
||||
|
||||
public static final String TARGET_LIST = "List of provisioning targets.";
|
||||
|
||||
public static final String TARGET_TYPE_LIST = "List of target types";
|
||||
|
||||
public static final String SM_LIST = "List of software modules.";
|
||||
|
||||
public static final String ROLLOUT_LIST = "list of rollouts";
|
||||
|
||||
@@ -0,0 +1,273 @@
|
||||
/**
|
||||
* 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.rest.mgmt.documentation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import io.qameta.allure.Description;
|
||||
import io.qameta.allure.Feature;
|
||||
import io.qameta.allure.Story;
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.model.TargetType;
|
||||
import org.eclipse.hawkbit.rest.documentation.AbstractApiRestDocumentation;
|
||||
import org.eclipse.hawkbit.rest.documentation.ApiModelPropertiesGeneric;
|
||||
import org.eclipse.hawkbit.rest.documentation.MgmtApiModelProperties;
|
||||
import org.eclipse.hawkbit.rest.util.JsonBuilder;
|
||||
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
|
||||
import org.json.JSONObject;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.restdocs.payload.JsonFieldType;
|
||||
|
||||
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.delete;
|
||||
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
|
||||
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post;
|
||||
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.put;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
|
||||
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
|
||||
import static org.springframework.restdocs.request.RequestDocumentation.pathParameters;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Documentation generation for Management API for {@link TargetType}.
|
||||
*
|
||||
*/
|
||||
@Feature("Spring Rest Docs Tests - TargetType")
|
||||
@Story("TargetTypes Resource")
|
||||
public class TargetTypesDocumentationTest extends AbstractApiRestDocumentation {
|
||||
@Override
|
||||
public String getResourceName() {
|
||||
return "targettypes";
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Handles the GET request of retrieving all target types within Hawkbit. Required Permission: "
|
||||
+ SpPermission.READ_TARGET)
|
||||
public void getTargetTypes() throws Exception {
|
||||
testdataFactory.findOrCreateTargetType("targetType1");
|
||||
testdataFactory.createTargetType("targetType2", Collections.singletonList(standardDsType));
|
||||
|
||||
mockMvc.perform(get(MgmtRestConstants.TARGETTYPE_V1_REQUEST_MAPPING).accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andDo(this.document.document(responseFields(
|
||||
fieldWithPath("size").type(JsonFieldType.NUMBER).description(ApiModelPropertiesGeneric.SIZE),
|
||||
fieldWithPath("total").description(ApiModelPropertiesGeneric.TOTAL_ELEMENTS),
|
||||
fieldWithPath("content").description(MgmtApiModelProperties.TARGET_TYPE_LIST),
|
||||
fieldWithPath("content[].id").description(ApiModelPropertiesGeneric.ITEM_ID),
|
||||
fieldWithPath("content[].name").description(ApiModelPropertiesGeneric.NAME),
|
||||
fieldWithPath("content[].description").description(ApiModelPropertiesGeneric.DESCRPTION),
|
||||
fieldWithPath("content[].colour").description(ApiModelPropertiesGeneric.COLOUR),
|
||||
fieldWithPath("content[].createdAt").description(ApiModelPropertiesGeneric.CREATED_AT),
|
||||
fieldWithPath("content[].createdBy").description(ApiModelPropertiesGeneric.CREATED_BY),
|
||||
fieldWithPath("content[].lastModifiedAt")
|
||||
.description(ApiModelPropertiesGeneric.LAST_MODIFIED_AT).type("Number"),
|
||||
fieldWithPath("content[].lastModifiedBy")
|
||||
.description(ApiModelPropertiesGeneric.LAST_MODIFIED_BY).type("String"),
|
||||
fieldWithPath("content[]._links.self").ignored())));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Handles the GET request of retrieving all target types within Hawkbit with a defined page size and "
|
||||
+ "offset, sorted by name in descending order and filtered down to all targets which name starts with 'targetType'. "
|
||||
+ "Required Permission: " + SpPermission.READ_TARGET)
|
||||
public void getTargetTypesWithParameters() throws Exception {
|
||||
testdataFactory.findOrCreateTargetType("targetType1");
|
||||
testdataFactory.createTargetType("targetType2", Collections.singletonList(standardDsType));
|
||||
|
||||
mockMvc.perform(get(MgmtRestConstants.TARGETTYPE_V1_REQUEST_MAPPING).accept(MediaType.APPLICATION_JSON)
|
||||
.param("offset", "0").param("limit", "2").param("sort", "name:ASC").param("q", "name==targetType*"))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
|
||||
.andDo(this.document.document(getFilterRequestParamter()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Handles the GET request for a single target type within Hawkbit. Required Permission: "
|
||||
+ SpPermission.READ_TARGET)
|
||||
public void getTargetType() throws Exception {
|
||||
TargetType testType = testdataFactory.createTargetType("TargetType", Collections.singletonList(standardDsType));
|
||||
|
||||
mockMvc.perform(get(MgmtRestConstants.TARGETTYPE_V1_REQUEST_MAPPING + "/{targetTypeId}", testType.getId())
|
||||
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andDo(this.document.document(
|
||||
pathParameters(
|
||||
parameterWithName("targetTypeId").description(ApiModelPropertiesGeneric.ITEM_ID)),
|
||||
responseFields(fieldWithPath("id").description(ApiModelPropertiesGeneric.ITEM_ID),
|
||||
fieldWithPath("name").description(ApiModelPropertiesGeneric.NAME),
|
||||
fieldWithPath("description").description(ApiModelPropertiesGeneric.DESCRPTION),
|
||||
fieldWithPath("colour").description(ApiModelPropertiesGeneric.COLOUR),
|
||||
fieldWithPath("createdAt").description(ApiModelPropertiesGeneric.CREATED_AT),
|
||||
fieldWithPath("createdBy").description(ApiModelPropertiesGeneric.CREATED_BY),
|
||||
fieldWithPath("lastModifiedAt").description(ApiModelPropertiesGeneric.LAST_MODIFIED_AT)
|
||||
.type("Number"),
|
||||
fieldWithPath("lastModifiedBy").description(ApiModelPropertiesGeneric.LAST_MODIFIED_BY)
|
||||
.type("String"),
|
||||
fieldWithPath("_links.compatibledistributionsettypes.href")
|
||||
.description(MgmtApiModelProperties.LINK_COMPATIBLE_DS_TYPES),
|
||||
fieldWithPath("_links.self").ignored())));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Handles the POST request for creating new target types within Hawkbit. The request body "
|
||||
+ "must always be a list of types. Required Permission: " + SpPermission.CREATE_TARGET)
|
||||
public void postTargetTypes() throws Exception {
|
||||
final List<TargetType> types = new ArrayList<>();
|
||||
types.add(entityFactory.targetType().create().name("targetType1").description("targetType1 description")
|
||||
.colour("#ffffff").build());
|
||||
types.add(entityFactory.targetType().create().name("targetType2").description("targetType2 description")
|
||||
.colour("#000000").compatible(Collections.singletonList(standardDsType.getId())).build());
|
||||
|
||||
mockMvc.perform(post(MgmtRestConstants.TARGETTYPE_V1_REQUEST_MAPPING)
|
||||
.content(JsonBuilder.targetTypesCreatableFieldsOnly(types)).contentType(MediaType.APPLICATION_JSON)
|
||||
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isCreated())
|
||||
.andDo(this.document.document(
|
||||
requestFields(requestFieldWithPath("[]name").description(ApiModelPropertiesGeneric.NAME),
|
||||
optionalRequestFieldWithPath("[]description")
|
||||
.description(ApiModelPropertiesGeneric.DESCRPTION),
|
||||
optionalRequestFieldWithPath("[]colour").description(ApiModelPropertiesGeneric.COLOUR),
|
||||
optionalRequestFieldWithPath("[]compatibledistributionsettypes")
|
||||
.description(MgmtApiModelProperties.COMPATIBLE_DS_TYPES)),
|
||||
responseFields(fieldWithPath("[]id").description(ApiModelPropertiesGeneric.ITEM_ID),
|
||||
fieldWithPath("[]name").description(ApiModelPropertiesGeneric.NAME),
|
||||
fieldWithPath("[]description").description(ApiModelPropertiesGeneric.DESCRPTION),
|
||||
fieldWithPath("[]colour").description(ApiModelPropertiesGeneric.COLOUR),
|
||||
fieldWithPath("[]createdAt").description(ApiModelPropertiesGeneric.CREATED_AT),
|
||||
fieldWithPath("[]createdBy").description(ApiModelPropertiesGeneric.CREATED_BY),
|
||||
fieldWithPath("[]lastModifiedAt")
|
||||
.description(ApiModelPropertiesGeneric.LAST_MODIFIED_AT).type("Number"),
|
||||
fieldWithPath("[]lastModifiedBy")
|
||||
.description(ApiModelPropertiesGeneric.LAST_MODIFIED_BY).type("String"),
|
||||
fieldWithPath("[]_links.self").ignored())));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Handles the DELETE request of retrieving a single target type within Hawkbit. Required Permission: "
|
||||
+ SpPermission.DELETE_TARGET)
|
||||
public void deleteTargetType() throws Exception {
|
||||
TargetType testType = testdataFactory.createTargetType("TargetType", Collections.singletonList(standardDsType));
|
||||
|
||||
mockMvc.perform(delete(MgmtRestConstants.TARGETTYPE_V1_REQUEST_MAPPING + "/{targetTypeId}", testType.getId()))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andDo(this.document.document(pathParameters(
|
||||
parameterWithName("targetTypeId").description(ApiModelPropertiesGeneric.ITEM_ID))));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Handles the PUT request for a single target type within Hawkbit. " + "Required Permission: "
|
||||
+ SpPermission.UPDATE_TARGET)
|
||||
public void putTargetType() throws Exception {
|
||||
TargetType testType = testdataFactory.createTargetType("targetType", Collections.singletonList(standardDsType));
|
||||
final String body = new JSONObject().put("description", "an updated description").put("name", "updatedTypeName")
|
||||
.put("colour", "#aaafff").toString();
|
||||
|
||||
this.mockMvc
|
||||
.perform(put(MgmtRestConstants.TARGETTYPE_V1_REQUEST_MAPPING + "/{targetTypeId}", testType.getId())
|
||||
.content(body).contentType(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andDo(this.document.document(
|
||||
pathParameters(
|
||||
parameterWithName("targetTypeId").description(ApiModelPropertiesGeneric.ITEM_ID)),
|
||||
requestFields(
|
||||
optionalRequestFieldWithPath("description")
|
||||
.description(ApiModelPropertiesGeneric.DESCRPTION),
|
||||
optionalRequestFieldWithPath("name").description(ApiModelPropertiesGeneric.NAME),
|
||||
optionalRequestFieldWithPath("colour").description(ApiModelPropertiesGeneric.COLOUR)),
|
||||
responseFields(fieldWithPath("id").description(ApiModelPropertiesGeneric.ITEM_ID),
|
||||
fieldWithPath("name").description(ApiModelPropertiesGeneric.NAME),
|
||||
fieldWithPath("description").description(ApiModelPropertiesGeneric.DESCRPTION),
|
||||
fieldWithPath("colour").description(ApiModelPropertiesGeneric.COLOUR),
|
||||
fieldWithPath("createdAt").description(ApiModelPropertiesGeneric.CREATED_AT),
|
||||
fieldWithPath("createdBy").description(ApiModelPropertiesGeneric.CREATED_BY),
|
||||
fieldWithPath("lastModifiedAt").description(ApiModelPropertiesGeneric.LAST_MODIFIED_AT)
|
||||
.type("Number"),
|
||||
fieldWithPath("lastModifiedBy").description(ApiModelPropertiesGeneric.LAST_MODIFIED_BY)
|
||||
.type("String"),
|
||||
fieldWithPath("_links.compatibledistributionsettypes.href")
|
||||
.description(MgmtApiModelProperties.LINK_COMPATIBLE_DS_TYPES),
|
||||
fieldWithPath("_links.self").ignored())));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Handles the GET request of retrieving the list of compatible distribution set types in that target type. "
|
||||
+ "Required Permission: " + SpPermission.READ_TARGET + " and " + SpPermission.READ_REPOSITORY)
|
||||
public void getCompatibleDistributionSetTypes() throws Exception {
|
||||
TargetType testType = testdataFactory.createTargetType("targetType", Collections.singletonList(standardDsType));
|
||||
|
||||
mockMvc.perform(
|
||||
get(MgmtRestConstants.TARGETTYPE_V1_REQUEST_MAPPING + "/{targetTypeId}/compatibledistributionsettypes",
|
||||
testType.getId()).accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
|
||||
|
||||
.andDo(this.document.document(
|
||||
pathParameters(
|
||||
parameterWithName("targetTypeId").description(ApiModelPropertiesGeneric.ITEM_ID)),
|
||||
responseFields(fieldWithPath("[]id").description(ApiModelPropertiesGeneric.ITEM_ID),
|
||||
fieldWithPath("[]key").description(MgmtApiModelProperties.DS_TYPE_KEY),
|
||||
fieldWithPath("[]name").description(ApiModelPropertiesGeneric.NAME),
|
||||
fieldWithPath("[]description").description(ApiModelPropertiesGeneric.DESCRPTION),
|
||||
fieldWithPath("[]createdAt").description(ApiModelPropertiesGeneric.CREATED_AT),
|
||||
fieldWithPath("[]createdBy").description(ApiModelPropertiesGeneric.CREATED_BY),
|
||||
fieldWithPath("[]lastModifiedAt")
|
||||
.description(ApiModelPropertiesGeneric.LAST_MODIFIED_AT).type("Number"),
|
||||
fieldWithPath("[]lastModifiedBy")
|
||||
.description(ApiModelPropertiesGeneric.LAST_MODIFIED_BY).type("String"),
|
||||
fieldWithPath("[]deleted").description(ApiModelPropertiesGeneric.DELETED),
|
||||
fieldWithPath("[]_links.self").ignored())));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Handles the POST request for adding a compatible distribution set type to a target type."
|
||||
+ " Required Permission: " + SpPermission.UPDATE_TARGET + " and " + SpPermission.READ_REPOSITORY)
|
||||
public void postCompatibleDistributionSetTypes() throws Exception {
|
||||
final DistributionSetType dsType1 = distributionSetTypeManagement.create(
|
||||
entityFactory.distributionSetType().create().key("test1").name("TestName1").description("Desc1"));
|
||||
final DistributionSetType dsType2 = distributionSetTypeManagement.create(
|
||||
entityFactory.distributionSetType().create().key("test2").name("TestName2").description("Desc2"));
|
||||
final TargetType targetType = testdataFactory.createTargetType("targetType",
|
||||
Collections.singletonList(standardDsType));
|
||||
|
||||
mockMvc.perform(
|
||||
post(MgmtRestConstants.TARGETTYPE_V1_REQUEST_MAPPING + "/{targetTypeId}/compatibledistributionsettypes",
|
||||
targetType.getId())
|
||||
.content("[{\"id\":" + dsType1.getId() + "},{\"id\":" + dsType2.getId() + "}]")
|
||||
.contentType(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andDo(this.document.document(
|
||||
pathParameters(
|
||||
parameterWithName("targetTypeId").description(ApiModelPropertiesGeneric.ITEM_ID)),
|
||||
requestFields(requestFieldWithPath("[]id").description(ApiModelPropertiesGeneric.ITEM_ID))));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Handles the DELETE request to unassign the list of compatible distribution set types in that target type. "
|
||||
+ SpPermission.UPDATE_TARGET + " and " + SpPermission.READ_REPOSITORY)
|
||||
public void deleteCompatibleDistributionSetType() throws Exception {
|
||||
final DistributionSetType dsType = distributionSetTypeManagement.create(
|
||||
entityFactory.distributionSetType().create().key("test1").name("TestName1").description("Desc1"));
|
||||
final TargetType targetType = testdataFactory.createTargetType("targetType", Collections.singletonList(dsType));
|
||||
|
||||
mockMvc.perform(delete(
|
||||
MgmtRestConstants.TARGETTYPE_V1_REQUEST_MAPPING
|
||||
+ "/{targetTypeId}/compatibledistributionsettypes/{distributionSetTypeId}",
|
||||
targetType.getId(), dsType.getId()).contentType(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andDo(this.document.document(pathParameters(
|
||||
parameterWithName("targetTypeId").description(ApiModelPropertiesGeneric.ITEM_ID),
|
||||
parameterWithName("distributionSetTypeId").description(ApiModelPropertiesGeneric.ITEM_ID))));
|
||||
|
||||
}
|
||||
}
|
||||
@@ -298,6 +298,11 @@ public class HawkbitSecurityProperties {
|
||||
*/
|
||||
private long maxArtifactStorage = 21_474_836_480L;
|
||||
|
||||
/**
|
||||
* Maximum number of distribution set types per target types
|
||||
*/
|
||||
private int maxDistributionSetTypesPerTargetType = 50;
|
||||
|
||||
private final Filter filter = new Filter();
|
||||
private final Filter uiFilter = new Filter();
|
||||
|
||||
@@ -439,6 +444,10 @@ public class HawkbitSecurityProperties {
|
||||
this.maxArtifactStorage = maxArtifactStorage;
|
||||
}
|
||||
|
||||
public int getMaxDistributionSetTypesPerTargetType() {
|
||||
return maxDistributionSetTypesPerTargetType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration for hawkBits DOS prevention filter. This is usually an
|
||||
* infrastructure topic (e.g. Web Application Firewall (WAF)) but might
|
||||
|
||||
Reference in New Issue
Block a user