Feature target metadata (#757)

* Defined the model for target matadata and the corresponding repository layer/management
* Added target metadata quotas incl enforcement
* Extended Target Mgmt REST API to allow for metadata CRUD operations
* Added migration scripts for each database
* Added back reference to target metadata in JpaTarget
* Added tests for target management, Mgmt REST API, target metadata RSQL, and REST documentation
* Updated asciidocs for target rest documentation
* Fix Allure imports and annotations
* Fix review findings

Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch-si.com>
Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>
This commit is contained in:
Bondar Bogdan
2018-10-29 11:28:34 +01:00
committed by Stefan Behl
parent 1cbde47370
commit 0cf4f8e8b9
37 changed files with 1886 additions and 216 deletions

View File

@@ -0,0 +1,36 @@
/**
* Copyright (c) 2018 Bosch Software Innovations 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;
/**
* Sort fields for TargetMetadata.
*
*/
public enum TargetMetadataFields implements FieldNameProvider {
/**
* The value field.
*/
VALUE("value"),
/**
* The key field.
*/
KEY("key");
private final String fieldName;
TargetMetadataFields(final String fieldName) {
this.fieldName = fieldName;
}
@Override
public String getFieldName() {
return fieldName;
}
}

View File

@@ -43,7 +43,8 @@ public interface EntityFactory {
DistributionSetBuilder distributionSet(); DistributionSetBuilder distributionSet();
/** /**
* Generates an {@link MetaData} element without persisting it. * Generates an {@link MetaData} element for distribution set without
* persisting it.
* *
* @param key * @param key
* {@link MetaData#getKey()} * {@link MetaData#getKey()}
@@ -52,7 +53,20 @@ public interface EntityFactory {
* *
* @return {@link MetaData} object * @return {@link MetaData} object
*/ */
MetaData generateMetadata(@Size(min = 1, max = MetaData.KEY_MAX_SIZE) @NotNull String key, MetaData generateDsMetadata(@Size(min = 1, max = MetaData.KEY_MAX_SIZE) @NotNull String key,
@Size(max = MetaData.VALUE_MAX_SIZE) String value);
/**
* Generates an {@link MetaData} element for target without persisting it.
*
* @param key
* {@link MetaData#getKey()}
* @param value
* {@link MetaData#getValue()}
*
* @return {@link MetaData} object
*/
MetaData generateTargetMetadata(@Size(min = 1, max = MetaData.KEY_MAX_SIZE) @NotNull String key,
@Size(max = MetaData.VALUE_MAX_SIZE) String value); @Size(max = MetaData.VALUE_MAX_SIZE) String value);
/** /**

View File

@@ -53,6 +53,11 @@ public interface QuotaManagement {
*/ */
int getMaxMetaDataEntriesPerDistributionSet(); int getMaxMetaDataEntriesPerDistributionSet();
/**
* @return maximum number of meta data entries per target
*/
int getMaxMetaDataEntriesPerTarget();
/** /**
* @return maximum number of software modules per distribution set * @return maximum number of software modules per distribution set
*/ */

View File

@@ -23,13 +23,16 @@ import org.eclipse.hawkbit.repository.builder.TargetCreate;
import org.eclipse.hawkbit.repository.builder.TargetUpdate; import org.eclipse.hawkbit.repository.builder.TargetUpdate;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException; import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException; import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException; import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.MetaData;
import org.eclipse.hawkbit.repository.model.RolloutGroup; import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.Tag; import org.eclipse.hawkbit.repository.model.Tag;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery; 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.TargetTag;
import org.eclipse.hawkbit.repository.model.TargetTagAssignmentResult; import org.eclipse.hawkbit.repository.model.TargetTagAssignmentResult;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
@@ -650,4 +653,116 @@ public interface TargetManagement {
* {@code false}: update of controller attributes not requested. * {@code false}: update of controller attributes not requested.
*/ */
boolean isControllerAttributesRequested(@NotEmpty String controllerId); boolean isControllerAttributesRequested(@NotEmpty String controllerId);
/**
* Creates a list of target meta data entries.
*
* @param controllerId
* {@link Target} controller id the metadata has to be created
* for
* @param metadata
* the meta data entries to create or update
* @return the updated or created target meta data entries
*
* @throws EntityNotFoundException
* if given target does not exist
*
* @throws EntityAlreadyExistsException
* in case one of the meta data entry already exists for the
* specific key
*
* @throws QuotaExceededException
* if the maximum number of {@link MetaData} entries is exceeded
* for the addressed {@link Target}
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
List<TargetMetadata> createMetaData(@NotEmpty String controllerId, @NotEmpty Collection<MetaData> metadata);
/**
* Deletes a target meta data entry.
*
* @param controllerId
* where meta data has to be deleted
* @param key
* of the meta data element
*
* @throws EntityNotFoundException
* if given target does not exist
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
void deleteMetaData(@NotEmpty String controllerId, @NotEmpty String key);
/**
* Finds all meta data by the given target id.
*
* @param pageable
* the page request to page the result
* @param controllerId
* the controller id to retrieve the meta data from
*
* @return a paged result of all meta data entries for a given target id
*
* @throws EntityNotFoundException
* if target with given ID does not exist
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
Page<TargetMetadata> findMetaDataByControllerId(@NotNull Pageable pageable, @NotEmpty String controllerId);
/**
* Finds all meta data by the given target id and query.
*
* @param pageable
* the page request to page the result
* @param controllerId
* the controller id to retrieve the meta data from
* @param rsqlParam
* rsql query string
*
* @return a paged result of all meta data entries for a given target id
*
* @throws RSQLParameterUnsupportedFieldException
* if a field in the RSQL string is used but not provided by the
* given {@code fieldNameProvider}
*
* @throws RSQLParameterSyntaxException
* if the RSQL syntax is wrong
*
* @throws EntityNotFoundException
* if target with given ID does not exist
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
Page<TargetMetadata> findMetaDataByControllerIdAndRsql(@NotNull Pageable pageable, @NotEmpty String controllerId,
@NotNull String rsqlParam);
/**
* Finds a single target meta data by its id.
*
* @param controllerId
* of the {@link Target}
* @param key
* of the meta data element
* @return the found TargetMetadata
*
* @throws EntityNotFoundException
* if target with given ID does not exist
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
Optional<TargetMetadata> getMetaDataByControllerId(@NotEmpty String controllerId, @NotEmpty String key);
/**
* Updates a target meta data value if corresponding entry exists.
*
* @param controllerId
* {@link Target} controller id of the meta data entry to be
* updated
* @param metadata
* meta data entry to be updated
* @return the updated meta data entry
*
* @throws EntityNotFoundException
* in case the meta data entry does not exists and cannot be
* updated
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
TargetMetadata updateMetaData(@NotEmpty String controllerId, @NotNull MetaData metadata);
} }

View File

@@ -70,11 +70,25 @@ public class EntityNotFoundException extends AbstractServerRtException {
* @param type * @param type
* of the entity that was not found * of the entity that was not found
* *
* @param enityId * @param entityId
* of the {@link BaseEntity} * of the {@link BaseEntity}
*/ */
public EntityNotFoundException(final Class<? extends BaseEntity> type, final Object enityId) { public EntityNotFoundException(final Class<? extends BaseEntity> type, final Object entityId) {
this(type.getSimpleName() + " with given identifier {" + enityId + "} does not exist."); this(type.getSimpleName() + " with given identifier {" + entityId + "} does not exist.");
}
/**
* Parameterized constructor for {@link MetaData} not found.
*
* @param type
* of the entity that was not found
* @param entityId
* of the {@link BaseEntity} the {@link MetaData} was for
* @param key
* for the {@link MetaData} entry
*/
public EntityNotFoundException(final Class<? extends MetaData> type, final Long entityId, final String key) {
this(type.getSimpleName() + " for given entity {" + entityId + "} and with key {" + key + "} does not exist.");
} }
/** /**
@@ -87,7 +101,7 @@ public class EntityNotFoundException extends AbstractServerRtException {
* @param key * @param key
* for the {@link MetaData} entry * for the {@link MetaData} entry
*/ */
public EntityNotFoundException(final Class<? extends MetaData> type, final Long enityId, final String key) { public EntityNotFoundException(final Class<? extends MetaData> type, final String enityId, final String key) {
this(type.getSimpleName() + " for given entity {" + enityId + "} and with key {" + key + "} does not exist."); this(type.getSimpleName() + " for given entity {" + enityId + "} and with key {" + key + "} does not exist.");
} }

View File

@@ -0,0 +1,26 @@
/**
* Copyright (c) 2018 Bosch Software Innovations 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;
/**
* {@link MetaData} of a {@link Target}.
*
*/
public interface TargetMetadata extends MetaData {
/**
* @return {@link Target} of this {@link MetaData} entry.
*/
Target getTarget();
@Override
default Long getEntityId() {
return getTarget().getId();
}
}

View File

@@ -58,6 +58,11 @@ public class PropertiesQuotaManagement implements QuotaManagement {
return securityProperties.getDos().getMaxMetaDataEntriesPerDistributionSet(); return securityProperties.getDos().getMaxMetaDataEntriesPerDistributionSet();
} }
@Override
public int getMaxMetaDataEntriesPerTarget() {
return securityProperties.getDos().getMaxMetaDataEntriesPerTarget();
}
@Override @Override
public int getMaxSoftwareModulesPerDistributionSet() { public int getMaxSoftwareModulesPerDistributionSet() {
return securityProperties.getDos().getMaxSoftwareModulesPerDistributionSet(); return securityProperties.getDos().getMaxSoftwareModulesPerDistributionSet();

View File

@@ -457,7 +457,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<DistributionSetMetadata> createMetaData(final long dsId, final Collection<MetaData> md) { public List<DistributionSetMetadata> createMetaData(final long dsId, final Collection<MetaData> md) {
md.forEach(meta -> checkAndThrowAlreadyIfDistributionSetMetadataExists( md.forEach(meta -> checkAndThrowIfDistributionSetMetadataAlreadyExists(
new DsMetadataCompositeKey(dsId, meta.getKey()))); new DsMetadataCompositeKey(dsId, meta.getKey())));
assertMetaDataQuota(dsId, md.size()); assertMetaDataQuota(dsId, md.size());
@@ -478,8 +478,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
private void assertSoftwareModuleQuota(final Long id, final int requested) { private void assertSoftwareModuleQuota(final Long id, final int requested) {
QuotaHelper.assertAssignmentQuota(id, requested, quotaManagement.getMaxSoftwareModulesPerDistributionSet(), QuotaHelper.assertAssignmentQuota(id, requested, quotaManagement.getMaxSoftwareModulesPerDistributionSet(),
SoftwareModule.class, DistributionSet.class, SoftwareModule.class, DistributionSet.class, softwareModuleRepository::countByAssignedToId);
softwareModuleRepository::countByAssignedToId);
} }
@Override @Override
@@ -676,7 +675,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
return distributionSetRepository.findAll(SpecificationsBuilder.combineWithAnd(specList), pageable); return distributionSetRepository.findAll(SpecificationsBuilder.combineWithAnd(specList), pageable);
} }
private void checkAndThrowAlreadyIfDistributionSetMetadataExists(final DsMetadataCompositeKey metadataId) { private void checkAndThrowIfDistributionSetMetadataAlreadyExists(final DsMetadataCompositeKey metadataId) {
if (distributionSetMetadataRepository.exists(metadataId)) { if (distributionSetMetadataRepository.exists(metadataId)) {
throw new EntityAlreadyExistsException( throw new EntityAlreadyExistsException(
"Metadata entry with key '" + metadataId.getKey() + "' already exists"); "Metadata entry with key '" + metadataId.getKey() + "' already exists");

View File

@@ -26,6 +26,7 @@ import org.eclipse.hawkbit.repository.jpa.builder.JpaSoftwareModuleTypeBuilder;
import org.eclipse.hawkbit.repository.jpa.builder.JpaTagBuilder; import org.eclipse.hawkbit.repository.jpa.builder.JpaTagBuilder;
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetBuilder; import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetBuilder;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetMetadata; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetMetadata;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetMetadata;
import org.eclipse.hawkbit.repository.model.MetaData; import org.eclipse.hawkbit.repository.model.MetaData;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@@ -57,10 +58,15 @@ public class JpaEntityFactory implements EntityFactory {
private SoftwareModuleMetadataBuilder softwareModuleMetadataBuilder; private SoftwareModuleMetadataBuilder softwareModuleMetadataBuilder;
@Override @Override
public MetaData generateMetadata(final String key, final String value) { public MetaData generateDsMetadata(final String key, final String value) {
return new JpaDistributionSetMetadata(key, StringUtils.trimWhitespace(value)); return new JpaDistributionSetMetadata(key, StringUtils.trimWhitespace(value));
} }
@Override
public MetaData generateTargetMetadata(final String key, final String value) {
return new JpaTargetMetadata(key, StringUtils.trimWhitespace(value));
}
@Override @Override
public DistributionSetTypeBuilder distributionSetType() { public DistributionSetTypeBuilder distributionSetType() {
return distributionSetTypeBuilder; return distributionSetTypeBuilder;

View File

@@ -26,13 +26,16 @@ import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root; import javax.persistence.criteria.Root;
import org.eclipse.hawkbit.repository.FilterParams; import org.eclipse.hawkbit.repository.FilterParams;
import org.eclipse.hawkbit.repository.QuotaManagement;
import org.eclipse.hawkbit.repository.TargetFields; import org.eclipse.hawkbit.repository.TargetFields;
import org.eclipse.hawkbit.repository.TargetManagement; import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.TargetMetadataFields;
import org.eclipse.hawkbit.repository.TimestampCalculator; import org.eclipse.hawkbit.repository.TimestampCalculator;
import org.eclipse.hawkbit.repository.builder.TargetCreate; import org.eclipse.hawkbit.repository.builder.TargetCreate;
import org.eclipse.hawkbit.repository.builder.TargetUpdate; import org.eclipse.hawkbit.repository.builder.TargetUpdate;
import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetAttributesRequestedEvent; import org.eclipse.hawkbit.repository.event.remote.TargetAttributesRequestedEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetCreate; import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetCreate;
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetUpdate; import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetUpdate;
@@ -40,15 +43,21 @@ import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor; import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget; 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.JpaTargetTag;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_; import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
import org.eclipse.hawkbit.repository.jpa.model.TargetMetadataCompositeKey;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility; import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder; import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications; import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications;
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.MetaData;
import org.eclipse.hawkbit.repository.model.RolloutGroup; import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery; 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.TargetTag;
import org.eclipse.hawkbit.repository.model.TargetTagAssignmentResult; import org.eclipse.hawkbit.repository.model.TargetTagAssignmentResult;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
@@ -83,8 +92,12 @@ public class JpaTargetManagement implements TargetManagement {
private final EntityManager entityManager; private final EntityManager entityManager;
private final QuotaManagement quotaManagement;
private final TargetRepository targetRepository; private final TargetRepository targetRepository;
private final TargetMetadataRepository targetMetadataRepository;
private final RolloutGroupRepository rolloutGroupRepository; private final RolloutGroupRepository rolloutGroupRepository;
private final DistributionSetRepository distributionSetRepository; private final DistributionSetRepository distributionSetRepository;
@@ -107,7 +120,8 @@ public class JpaTargetManagement implements TargetManagement {
private final Database database; private final Database database;
JpaTargetManagement(final EntityManager entityManager, final TargetRepository targetRepository, JpaTargetManagement(final EntityManager entityManager, final QuotaManagement quotaManagement,
final TargetRepository targetRepository, final TargetMetadataRepository targetMetadataRepository,
final RolloutGroupRepository rolloutGroupRepository, final RolloutGroupRepository rolloutGroupRepository,
final DistributionSetRepository distributionSetRepository, final DistributionSetRepository distributionSetRepository,
final TargetFilterQueryRepository targetFilterQueryRepository, final TargetFilterQueryRepository targetFilterQueryRepository,
@@ -116,7 +130,9 @@ public class JpaTargetManagement implements TargetManagement {
final TenantAware tenantAware, final AfterTransactionCommitExecutor afterCommit, final TenantAware tenantAware, final AfterTransactionCommitExecutor afterCommit,
final VirtualPropertyReplacer virtualPropertyReplacer, final Database database) { final VirtualPropertyReplacer virtualPropertyReplacer, final Database database) {
this.entityManager = entityManager; this.entityManager = entityManager;
this.quotaManagement = quotaManagement;
this.targetRepository = targetRepository; this.targetRepository = targetRepository;
this.targetMetadataRepository = targetMetadataRepository;
this.rolloutGroupRepository = rolloutGroupRepository; this.rolloutGroupRepository = rolloutGroupRepository;
this.distributionSetRepository = distributionSetRepository; this.distributionSetRepository = distributionSetRepository;
this.targetFilterQueryRepository = targetFilterQueryRepository; this.targetFilterQueryRepository = targetFilterQueryRepository;
@@ -135,6 +151,11 @@ public class JpaTargetManagement implements TargetManagement {
return targetRepository.findByControllerId(controllerId); return targetRepository.findByControllerId(controllerId);
} }
private JpaTarget getByControllerIdAndThrowIfNotFound(final String controllerId) {
return targetRepository.findByControllerId(controllerId).map(JpaTarget.class::cast)
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
}
@Override @Override
public List<Target> getByControllerID(final Collection<String> controllerIDs) { public List<Target> getByControllerID(final Collection<String> controllerIDs) {
return Collections.unmodifiableList( return Collections.unmodifiableList(
@@ -146,6 +167,121 @@ public class JpaTargetManagement implements TargetManagement {
return targetRepository.count(); return targetRepository.count();
} }
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<TargetMetadata> createMetaData(final String controllerId, final Collection<MetaData> md) {
final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId);
md.forEach(meta -> checkAndThrowIfTargetMetadataAlreadyExists(
new TargetMetadataCompositeKey(target.getId(), meta.getKey())));
assertMetaDataQuota(target.getId(), md.size());
final JpaTarget updatedTarget = touch(target);
return Collections.unmodifiableList(md.stream()
.map(meta -> targetMetadataRepository
.save(new JpaTargetMetadata(meta.getKey(), meta.getValue(), updatedTarget)))
.collect(Collectors.toList()));
}
private void checkAndThrowIfTargetMetadataAlreadyExists(final TargetMetadataCompositeKey metadataId) {
if (targetMetadataRepository.exists(metadataId)) {
throw new EntityAlreadyExistsException(
"Metadata entry with key '" + metadataId.getKey() + "' already exists");
}
}
private void assertMetaDataQuota(final Long targetId, final int requested) {
QuotaHelper.assertAssignmentQuota(targetId, requested, quotaManagement.getMaxMetaDataEntriesPerTarget(),
TargetMetadata.class, Target.class, targetMetadataRepository::countByTargetId);
}
private JpaTarget touch(final JpaTarget target) {
// merge base target so optLockRevision gets updated and audit
// log written because modifying metadata is modifying the base
// target itself for auditing purposes.
final JpaTarget result = entityManager.merge(target);
result.setLastModifiedAt(0L);
return targetRepository.save(result);
}
private JpaTarget touch(final String controllerId) {
return touch(getByControllerIdAndThrowIfNotFound(controllerId));
}
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public TargetMetadata updateMetaData(final String controllerId, final MetaData md) {
// check if exists otherwise throw entity not found exception
final JpaTargetMetadata toUpdate = (JpaTargetMetadata) getMetaDataByControllerId(controllerId, md.getKey())
.orElseThrow(() -> new EntityNotFoundException(TargetMetadata.class, controllerId, md.getKey()));
toUpdate.setValue(md.getValue());
// touch it to update the lock revision because we are modifying the
// target indirectly
touch(controllerId);
return targetMetadataRepository.save(toUpdate);
}
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteMetaData(final String controllerId, final String key) {
final JpaTargetMetadata metadata = (JpaTargetMetadata) getMetaDataByControllerId(controllerId, key)
.orElseThrow(() -> new EntityNotFoundException(TargetMetadata.class, controllerId, key));
touch(controllerId);
targetMetadataRepository.delete(metadata.getId());
}
@Override
public Page<TargetMetadata> findMetaDataByControllerId(final Pageable pageable, final String controllerId) {
final Long targetId = getByControllerIdAndThrowIfNotFound(controllerId).getId();
return convertMdPage(
targetMetadataRepository
.findAll(
(Specification<JpaTargetMetadata>) (root, query, cb) -> cb
.equal(root.get(JpaTargetMetadata_.target).get(JpaTarget_.id), targetId),
pageable),
pageable);
}
private static Page<TargetMetadata> convertMdPage(final Page<JpaTargetMetadata> findAll, final Pageable pageable) {
return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, findAll.getTotalElements());
}
@Override
public Page<TargetMetadata> findMetaDataByControllerIdAndRsql(final Pageable pageable, final String controllerId,
final String rsqlParam) {
final Long targetId = getByControllerIdAndThrowIfNotFound(controllerId).getId();
final Specification<JpaTargetMetadata> spec = RSQLUtility.parse(rsqlParam, TargetMetadataFields.class,
virtualPropertyReplacer, database);
return convertMdPage(targetMetadataRepository.findAll((Specification<JpaTargetMetadata>) (root, query, cb) -> cb
.and(cb.equal(root.get(JpaTargetMetadata_.target).get(JpaTarget_.id), targetId),
spec.toPredicate(root, query, cb)),
pageable), pageable);
}
@Override
public Optional<TargetMetadata> getMetaDataByControllerId(final String controllerId, final String key) {
final Long targetId = getByControllerIdAndThrowIfNotFound(controllerId).getId();
return Optional.ofNullable(targetMetadataRepository.findOne(new TargetMetadataCompositeKey(targetId, key)));
}
@Override @Override
public Slice<Target> findAll(final Pageable pageable) { public Slice<Target> findAll(final Pageable pageable) {
return convertPage(criteriaNoCountDao.findAll(pageable, JpaTarget.class), pageable); return convertPage(criteriaNoCountDao.findAll(pageable, JpaTarget.class), pageable);
@@ -178,8 +314,7 @@ public class JpaTargetManagement implements TargetManagement {
public Target update(final TargetUpdate u) { public Target update(final TargetUpdate u) {
final JpaTargetUpdate update = (JpaTargetUpdate) u; final JpaTargetUpdate update = (JpaTargetUpdate) u;
final JpaTarget target = (JpaTarget) targetRepository.findByControllerId(update.getControllerId()) final JpaTarget target = getByControllerIdAndThrowIfNotFound(update.getControllerId());
.orElseThrow(() -> new EntityNotFoundException(Target.class, update.getControllerId()));
update.getName().ifPresent(target::setName); update.getName().ifPresent(target::setName);
update.getDescription().ifPresent(target::setDescription); update.getDescription().ifPresent(target::setDescription);
@@ -214,8 +349,7 @@ public class JpaTargetManagement implements TargetManagement {
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteByControllerID(final String controllerID) { public void deleteByControllerID(final String controllerID) {
final Target target = targetRepository.findByControllerId(controllerID) final Target target = getByControllerIdAndThrowIfNotFound(controllerID);
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerID));
targetRepository.delete(target.getId()); targetRepository.delete(target.getId());
} }
@@ -418,8 +552,7 @@ public class JpaTargetManagement implements TargetManagement {
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Target unAssignTag(final String controllerID, final long targetTagId) { public Target unAssignTag(final String controllerID, final long targetTagId) {
final JpaTarget target = (JpaTarget) targetRepository.findByControllerId(controllerID) final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerID);
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerID));
final TargetTag tag = targetTagRepository.findById(targetTagId) final TargetTag tag = targetTagRepository.findById(targetTagId)
.orElseThrow(() -> new EntityNotFoundException(TargetTag.class, targetTagId)); .orElseThrow(() -> new EntityNotFoundException(TargetTag.class, targetTagId));
@@ -635,16 +768,14 @@ public class JpaTargetManagement implements TargetManagement {
@Override @Override
public Map<String, String> getControllerAttributes(final String controllerId) { public Map<String, String> getControllerAttributes(final String controllerId) {
final JpaTarget target = (JpaTarget) getByControllerID(controllerId) final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId);
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
return target.getControllerAttributes(); return target.getControllerAttributes();
} }
@Override @Override
public void requestControllerAttributes(final String controllerId) { public void requestControllerAttributes(final String controllerId) {
final JpaTarget target = (JpaTarget) getByControllerID(controllerId) final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId);
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
target.setRequestControllerAttributes(true); target.setRequestControllerAttributes(true);
@@ -655,8 +786,7 @@ public class JpaTargetManagement implements TargetManagement {
@Override @Override
public boolean isControllerAttributesRequested(final String controllerId) { public boolean isControllerAttributesRequested(final String controllerId) {
final JpaTarget target = (JpaTarget) getByControllerID(controllerId) final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId);
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
return target.isRequestControllerAttributes(); return target.isRequestControllerAttributes();
} }

View File

@@ -449,7 +449,8 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
*/ */
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
TargetManagement targetManagement(final EntityManager entityManager, final TargetRepository targetRepository, TargetManagement targetManagement(final EntityManager entityManager, final QuotaManagement quotaManagement,
final TargetRepository targetRepository, final TargetMetadataRepository targetMetadataRepository,
final RolloutGroupRepository rolloutGroupRepository, final RolloutGroupRepository rolloutGroupRepository,
final DistributionSetRepository distributionSetRepository, final DistributionSetRepository distributionSetRepository,
final TargetFilterQueryRepository targetFilterQueryRepository, final TargetFilterQueryRepository targetFilterQueryRepository,
@@ -457,10 +458,10 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
final ApplicationEventPublisher eventPublisher, final ApplicationContext applicationContext, final ApplicationEventPublisher eventPublisher, final ApplicationContext applicationContext,
final TenantAware tenantAware, final AfterTransactionCommitExecutor afterCommit, final TenantAware tenantAware, final AfterTransactionCommitExecutor afterCommit,
final VirtualPropertyReplacer virtualPropertyReplacer, final JpaProperties properties) { final VirtualPropertyReplacer virtualPropertyReplacer, final JpaProperties properties) {
return new JpaTargetManagement(entityManager, targetRepository, rolloutGroupRepository, return new JpaTargetManagement(entityManager, quotaManagement, targetRepository, targetMetadataRepository,
distributionSetRepository, targetFilterQueryRepository, targetTagRepository, criteriaNoCountDao, rolloutGroupRepository, distributionSetRepository, targetFilterQueryRepository, targetTagRepository,
eventPublisher, applicationContext, tenantAware, afterCommit, virtualPropertyReplacer, criteriaNoCountDao, eventPublisher, applicationContext, tenantAware, afterCommit,
properties.getDatabase()); virtualPropertyReplacer, properties.getDatabase());
} }
/** /**

View File

@@ -0,0 +1,36 @@
/**
* Copyright (c) 2015 Bosch Software Innovations 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.JpaTargetMetadata;
import org.eclipse.hawkbit.repository.jpa.model.TargetMetadataCompositeKey;
import org.eclipse.hawkbit.repository.model.TargetMetadata;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;
/**
* {@link TargetMetadata} repository.
*/
@Transactional(readOnly = true)
public interface TargetMetadataRepository
extends PagingAndSortingRepository<JpaTargetMetadata, TargetMetadataCompositeKey>,
JpaSpecificationExecutor<JpaTargetMetadata> {
/**
* Counts the meta data entries that match the given target ID.
*
* @param id
* of the target.
*
* @return The number of matching meta data entries.
*/
long countByTargetId(@Param("id") Long id);
}

View File

@@ -54,6 +54,7 @@ import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.PollStatus; import org.eclipse.hawkbit.repository.model.PollStatus;
import org.eclipse.hawkbit.repository.model.Target; 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.TargetTag;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder; import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
@@ -167,6 +168,10 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
@Column(name = "request_controller_attributes", nullable = false) @Column(name = "request_controller_attributes", nullable = false)
private boolean requestControllerAttributes = true; private boolean requestControllerAttributes = true;
@CascadeOnDelete
@OneToMany(mappedBy = "target", fetch = FetchType.LAZY, targetEntity = JpaTargetMetadata.class)
private List<TargetMetadata> metadata;
/** /**
* Constructor. * Constructor.
* *
@@ -351,6 +356,14 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
return requestControllerAttributes; return requestControllerAttributes;
} }
public List<TargetMetadata> getMetadata() {
if (metadata == null) {
return Collections.emptyList();
}
return Collections.unmodifiableList(metadata);
}
@Override @Override
public String toString() { public String toString() {
return "JpaTarget [controllerId=" + controllerId + ", revision=" + getOptLockRevision() + ", id=" + getId() return "JpaTarget [controllerId=" + controllerId + ", revision=" + getOptLockRevision() + ", id=" + getId()

View File

@@ -0,0 +1,109 @@
/**
* Copyright (c) 2018 Bosch Software Innovations 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 javax.persistence.ConstraintMode;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.ForeignKey;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetMetadata;
/**
* Meta data for {@link Target}.
*
*/
@IdClass(TargetMetadataCompositeKey.class)
@Entity
@Table(name = "sp_target_metadata")
public class JpaTargetMetadata extends AbstractJpaMetaData implements TargetMetadata {
private static final long serialVersionUID = 1L;
@Id
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "target_id", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_metadata_target"))
private JpaTarget target;
public JpaTargetMetadata() {
// default public constructor for JPA
}
/**
* Creates a single metadata entry with the given key and value.
*
* @param key
* of the meta data entry
* @param value
* of the meta data entry
*/
public JpaTargetMetadata(final String key, final String value) {
super(key, value);
}
/**
* Creates a single metadata entry with the given key and value for the
* given {@link Target}.
*
* @param key
* of the meta data entry
* @param value
* of the meta data entry
* @param target
* the meta data entry is associated with
*/
public JpaTargetMetadata(final String key, final String value, final Target target) {
super(key, value);
this.target = (JpaTarget) target;
}
public TargetMetadataCompositeKey getId() {
return new TargetMetadataCompositeKey(target.getId(), getKey());
}
public void setTarget(final Target target) {
this.target = (JpaTarget) target;
}
@Override
public Target getTarget() {
return target;
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((target == null) ? 0 : target.hashCode());
return result;
}
@Override
// exception squid:S2259 - obj is checked for null in super
@SuppressWarnings("squid:S2259")
public boolean equals(final Object obj) {
if (!super.equals(obj)) {
return false;
}
final JpaTargetMetadata other = (JpaTargetMetadata) obj;
if (target == null) {
if (other.target != null) {
return false;
}
} else if (!target.equals(other.target)) {
return false;
}
return true;
}
}

View File

@@ -0,0 +1,90 @@
/**
* Copyright (c) 2018 Bosch Software Innovations 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 java.io.Serializable;
import java.util.Objects;
/**
* The Target Metadata composite key which contains the meta data key and the ID
* of the Target itself.
*
*/
public final class TargetMetadataCompositeKey implements Serializable {
private static final long serialVersionUID = 1L;
private String key;
private Long target;
public TargetMetadataCompositeKey() {
// Default constructor for JPA.
}
/**
* @param target
* the target Id for this meta data
* @param key
* the key of the meta data
*/
public TargetMetadataCompositeKey(final Long target, final String key) {
this.target = target;
this.key = key;
}
public String getKey() {
return key;
}
public void setKey(final String key) {
this.key = key;
}
public Long getTargetId() {
return target;
}
public void setTargetId(final Long target) {
this.target = target;
}
@Override
public int hashCode() {
return Objects.hash(target, key);
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final TargetMetadataCompositeKey other = (TargetMetadataCompositeKey) obj;
if (target == null) {
if (other.target != null) {
return false;
}
} else if (!target.equals(other.target)) {
return false;
}
if (key == null) {
if (other.key != null) {
return false;
}
} else if (!key.equals(other.key)) {
return false;
}
return true;
}
}

View File

@@ -0,0 +1,9 @@
CREATE TABLE sp_target_metadata
(
meta_key VARCHAR(128) NOT NULL,
meta_value VARCHAR(4000),
target_id BIGINT NOT NULL,
PRIMARY KEY (meta_key, target_id)
);
ALTER TABLE sp_target_metadata ADD CONSTRAINT fk_metadata_target FOREIGN KEY (target_id) REFERENCES sp_target (id) ON DELETE CASCADE;

View File

@@ -0,0 +1,12 @@
create table sp_target_metadata (
meta_key varchar(128) not null,
meta_value varchar(4000),
target_id bigint not null,
primary key (target_id, meta_key)
);
alter table sp_target_metadata
add constraint fk_metadata_target
foreign key (target_id)
references sp_target
on delete cascade;

View File

@@ -0,0 +1,12 @@
create table sp_target_metadata (
meta_key varchar(128) not null,
meta_value varchar(4000),
target_id bigint not null,
primary key (target_id, meta_key)
);
alter table sp_target_metadata
add constraint fk_metadata_target
foreign key (target_id)
references sp_target (id)
on delete cascade;

View File

@@ -0,0 +1,9 @@
CREATE TABLE sp_target_metadata
(
meta_key VARCHAR(128) NOT NULL,
meta_value VARCHAR(4000) NULL,
target_id NUMERIC(19) NOT NULL,
PRIMARY KEY (meta_key, target_id)
);
ALTER TABLE sp_target_metadata ADD CONSTRAINT fk_metadata_target FOREIGN KEY (target_id) REFERENCES sp_target (id) ON DELETE CASCADE;

View File

@@ -136,7 +136,7 @@ public class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
"DistributionSetType"); "DistributionSetType");
verifyThrownExceptionBy(() -> distributionSetManagement.createMetaData(NOT_EXIST_IDL, verifyThrownExceptionBy(() -> distributionSetManagement.createMetaData(NOT_EXIST_IDL,
Arrays.asList(entityFactory.generateMetadata("123", "123"))), "DistributionSet"); Arrays.asList(entityFactory.generateDsMetadata("123", "123"))), "DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.delete(Arrays.asList(NOT_EXIST_IDL)), verifyThrownExceptionBy(() -> distributionSetManagement.delete(Arrays.asList(NOT_EXIST_IDL)),
"DistributionSet"); "DistributionSet");
@@ -167,10 +167,10 @@ public class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
"DistributionSet"); "DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.updateMetaData(NOT_EXIST_IDL, verifyThrownExceptionBy(() -> distributionSetManagement.updateMetaData(NOT_EXIST_IDL,
entityFactory.generateMetadata("xxx", "xxx")), "DistributionSet"); entityFactory.generateDsMetadata("xxx", "xxx")), "DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.updateMetaData(set.getId(), verifyThrownExceptionBy(() -> distributionSetManagement.updateMetaData(set.getId(),
entityFactory.generateMetadata(NOT_EXIST_ID, "xxx")), "DistributionSetMetadata"); entityFactory.generateDsMetadata(NOT_EXIST_ID, "xxx")), "DistributionSetMetadata");
} }
@Test @Test
@@ -553,7 +553,7 @@ public class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
// update the DS metadata // update the DS metadata
final JpaDistributionSetMetadata updated = (JpaDistributionSetMetadata) distributionSetManagement final JpaDistributionSetMetadata updated = (JpaDistributionSetMetadata) distributionSetManagement
.updateMetaData(ds.getId(), entityFactory.generateMetadata(knownKey, knownUpdateValue)); .updateMetaData(ds.getId(), entityFactory.generateDsMetadata(knownKey, knownUpdateValue));
// we are updating the sw meta data so also modifying the base software // we are updating the sw meta data so also modifying the base software
// module so opt lock // module so opt lock
// revision must be three // revision must be three

View File

@@ -41,13 +41,17 @@ import org.eclipse.hawkbit.repository.event.remote.entity.TargetTagCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException; import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.InvalidTargetAddressException; import org.eclipse.hawkbit.repository.exception.InvalidTargetAddressException;
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
import org.eclipse.hawkbit.repository.exception.TenantNotExistException; import org.eclipse.hawkbit.repository.exception.TenantNotExistException;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget; import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetMetadata;
import org.eclipse.hawkbit.repository.model.Action.Status; import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult; import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
import org.eclipse.hawkbit.repository.model.MetaData;
import org.eclipse.hawkbit.repository.model.Tag; import org.eclipse.hawkbit.repository.model.Tag;
import org.eclipse.hawkbit.repository.model.Target; 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.TargetTag;
import org.eclipse.hawkbit.repository.test.matcher.Expect; import org.eclipse.hawkbit.repository.test.matcher.Expect;
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents; import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
@@ -73,10 +77,12 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Verifies that management get access react as specified on calls for non existing entities by means " @Description("Verifies that management get access react as specified on calls for non existing entities by means "
+ "of Optional not present.") + "of Optional not present.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) }) @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1) })
public void nonExistingEntityAccessReturnsNotPresent() { public void nonExistingEntityAccessReturnsNotPresent() {
final Target target = testdataFactory.createTarget();
assertThat(targetManagement.getByControllerID(NOT_EXIST_ID)).isNotPresent(); assertThat(targetManagement.getByControllerID(NOT_EXIST_ID)).isNotPresent();
assertThat(targetManagement.get(NOT_EXIST_IDL)).isNotPresent(); assertThat(targetManagement.get(NOT_EXIST_IDL)).isNotPresent();
assertThat(targetManagement.getMetaDataByControllerId(target.getControllerId(), NOT_EXIST_ID)).isNotPresent();
} }
@Test @Test
@@ -89,8 +95,10 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest {
final Target target = testdataFactory.createTarget(); final Target target = testdataFactory.createTarget();
verifyThrownExceptionBy( verifyThrownExceptionBy(
() -> targetManagement.assignTag(Collections.singletonList(target.getControllerId()), NOT_EXIST_IDL), "TargetTag"); () -> targetManagement.assignTag(Collections.singletonList(target.getControllerId()), NOT_EXIST_IDL),
verifyThrownExceptionBy(() -> targetManagement.assignTag(Collections.singletonList(NOT_EXIST_ID), tag.getId()), "Target"); "TargetTag");
verifyThrownExceptionBy(() -> targetManagement.assignTag(Collections.singletonList(NOT_EXIST_ID), tag.getId()),
"Target");
verifyThrownExceptionBy(() -> targetManagement.findByTag(PAGE, NOT_EXIST_IDL), "TargetTag"); verifyThrownExceptionBy(() -> targetManagement.findByTag(PAGE, NOT_EXIST_IDL), "TargetTag");
verifyThrownExceptionBy(() -> targetManagement.findByRsqlAndTag(PAGE, "name==*", NOT_EXIST_IDL), "TargetTag"); verifyThrownExceptionBy(() -> targetManagement.findByRsqlAndTag(PAGE, "name==*", NOT_EXIST_IDL), "TargetTag");
@@ -123,16 +131,31 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest {
() -> targetManagement.findByInstalledDistributionSetAndRsql(PAGE, NOT_EXIST_IDL, "name==*"), () -> targetManagement.findByInstalledDistributionSetAndRsql(PAGE, NOT_EXIST_IDL, "name==*"),
"DistributionSet"); "DistributionSet");
verifyThrownExceptionBy(() -> targetManagement
.toggleTagAssignment(Collections.singletonList(target.getControllerId()), NOT_EXIST_ID), "TargetTag");
verifyThrownExceptionBy( verifyThrownExceptionBy(
() -> targetManagement.toggleTagAssignment(Collections.singletonList(target.getControllerId()), NOT_EXIST_ID), () -> targetManagement.toggleTagAssignment(Collections.singletonList(NOT_EXIST_ID), tag.getName()),
"TargetTag");
verifyThrownExceptionBy(() -> targetManagement.toggleTagAssignment(Collections.singletonList(NOT_EXIST_ID), tag.getName()),
"Target"); "Target");
verifyThrownExceptionBy(() -> targetManagement.unAssignTag(NOT_EXIST_ID, tag.getId()), "Target"); verifyThrownExceptionBy(() -> targetManagement.unAssignTag(NOT_EXIST_ID, tag.getId()), "Target");
verifyThrownExceptionBy(() -> targetManagement.unAssignTag(target.getControllerId(), NOT_EXIST_IDL), verifyThrownExceptionBy(() -> targetManagement.unAssignTag(target.getControllerId(), NOT_EXIST_IDL),
"TargetTag"); "TargetTag");
verifyThrownExceptionBy(() -> targetManagement.update(entityFactory.target().update(NOT_EXIST_ID)), "Target"); verifyThrownExceptionBy(() -> targetManagement.update(entityFactory.target().update(NOT_EXIST_ID)), "Target");
verifyThrownExceptionBy(() -> targetManagement.createMetaData(NOT_EXIST_ID,
Arrays.asList(entityFactory.generateTargetMetadata("123", "123"))), "Target");
verifyThrownExceptionBy(() -> targetManagement.deleteMetaData(NOT_EXIST_ID, "xxx"), "Target");
verifyThrownExceptionBy(() -> targetManagement.deleteMetaData(target.getControllerId(), NOT_EXIST_ID),
"TargetMetadata");
verifyThrownExceptionBy(() -> targetManagement.getMetaDataByControllerId(NOT_EXIST_ID, "xxx"), "Target");
verifyThrownExceptionBy(() -> targetManagement.findMetaDataByControllerId(PAGE, NOT_EXIST_ID), "Target");
verifyThrownExceptionBy(() -> targetManagement.findMetaDataByControllerIdAndRsql(PAGE, NOT_EXIST_ID, "name==*"),
"Target");
verifyThrownExceptionBy(
() -> targetManagement.updateMetaData(NOT_EXIST_ID, entityFactory.generateTargetMetadata("xxx", "xxx")),
"Target");
verifyThrownExceptionBy(() -> targetManagement.updateMetaData(target.getControllerId(),
entityFactory.generateTargetMetadata(NOT_EXIST_ID, "xxx")), "TargetMetadata");
} }
@Test @Test
@@ -143,8 +166,9 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest {
.create(entityFactory.target().create().controllerId("targetWithSecurityToken").securityToken("token")); .create(entityFactory.target().create().controllerId("targetWithSecurityToken").securityToken("token"));
// retrieve security token only with READ_TARGET_SEC_TOKEN permission // retrieve security token only with READ_TARGET_SEC_TOKEN permission
final String securityTokenWithReadPermission = securityRule.runAs(WithSpringAuthorityRule final String securityTokenWithReadPermission = securityRule.runAs(
.withUser("OnlyTargetReadPermission", false, SpPermission.READ_TARGET_SEC_TOKEN), createdTarget::getSecurityToken); WithSpringAuthorityRule.withUser("OnlyTargetReadPermission", false, SpPermission.READ_TARGET_SEC_TOKEN),
createdTarget::getSecurityToken);
// retrieve security token as system code execution // retrieve security token as system code execution
final String securityTokenAsSystemCode = systemSecurityContext.runAsSystem(createdTarget::getSecurityToken); final String securityTokenAsSystemCode = systemSecurityContext.runAsSystem(createdTarget::getSecurityToken);
@@ -889,4 +913,138 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest {
assertThat(targetManagement.isControllerAttributesRequested(knownTargetId)).isTrue(); assertThat(targetManagement.isControllerAttributesRequested(knownTargetId)).isTrue();
} }
@Test
@Description("Checks that metadata for a target can be created.")
public void createTargetMetadata() {
final String knownKey = "targetMetaKnownKey";
final String knownValue = "targetMetaKnownValue";
final Target target = testdataFactory.createTarget("targetIdWithMetadata");
final JpaTargetMetadata createdMetadata = insertTargetMetadata(knownKey, target, knownValue);
assertThat(createdMetadata).isNotNull();
assertThat(createdMetadata.getId().getKey()).isEqualTo(knownKey);
assertThat(createdMetadata.getTarget().getControllerId()).isEqualTo(target.getControllerId());
assertThat(createdMetadata.getTarget().getId()).isEqualTo(target.getId());
assertThat(createdMetadata.getValue()).isEqualTo(knownValue);
}
private JpaTargetMetadata insertTargetMetadata(final String knownKey, final Target target,
final String knownValue) {
final JpaTargetMetadata metadata = new JpaTargetMetadata(knownKey, knownValue, target);
return (JpaTargetMetadata) targetManagement
.createMetaData(target.getControllerId(), Collections.singletonList(metadata)).get(0);
}
@Test
@Description("Verifies the enforcement of the metadata quota per target.")
public void createTargetMetadataUntilQuotaIsExceeded() {
// add meta data one by one
final Target target1 = testdataFactory.createTarget("target1");
final int maxMetaData = quotaManagement.getMaxMetaDataEntriesPerTarget();
for (int i = 0; i < maxMetaData; ++i) {
assertThat(insertTargetMetadata("k" + i, target1, "v" + i)).isNotNull();
}
// quota exceeded
assertThatExceptionOfType(QuotaExceededException.class)
.isThrownBy(() -> insertTargetMetadata("k" + maxMetaData, target1, "v" + maxMetaData));
// add multiple meta data entries at once
final Target target2 = testdataFactory.createTarget("target2");
final List<MetaData> metaData2 = new ArrayList<>();
for (int i = 0; i < maxMetaData + 1; ++i) {
metaData2.add(new JpaTargetMetadata("k" + i, "v" + i, target2));
}
// verify quota is exceeded
assertThatExceptionOfType(QuotaExceededException.class)
.isThrownBy(() -> targetManagement.createMetaData(target2.getControllerId(), metaData2));
// add some meta data entries
final Target target3 = testdataFactory.createTarget("target3");
final int firstHalf = Math.round(maxMetaData / 2);
for (int i = 0; i < firstHalf; ++i) {
insertTargetMetadata("k" + i, target3, "v" + i);
}
// add too many data entries
final int secondHalf = maxMetaData - firstHalf;
final List<MetaData> metaData3 = new ArrayList<>();
for (int i = 0; i < secondHalf + 1; ++i) {
metaData3.add(new JpaTargetMetadata("kk" + i, "vv" + i, target3));
}
// verify quota is exceeded
assertThatExceptionOfType(QuotaExceededException.class)
.isThrownBy(() -> targetManagement.createMetaData(target3.getControllerId(), metaData3));
}
@Test
@WithUser(allSpPermissions = true)
@Description("Checks that metadata for a target can be updated.")
public void updateTargetMetadata() throws InterruptedException {
final String knownKey = "myKnownKey";
final String knownValue = "myKnownValue";
final String knownUpdateValue = "myNewUpdatedValue";
// create a target
final Target target = testdataFactory.createTarget("target1");
// initial opt lock revision must be zero
assertThat(target.getOptLockRevision()).isEqualTo(1);
// create target meta data entry
insertTargetMetadata(knownKey, target, knownValue);
Target changedLockRevisionTarget = targetManagement.get(target.getId()).get();
assertThat(changedLockRevisionTarget.getOptLockRevision()).isEqualTo(2);
Thread.sleep(100);
// update the target metadata
final JpaTargetMetadata updated = (JpaTargetMetadata) targetManagement.updateMetaData(target.getControllerId(),
entityFactory.generateTargetMetadata(knownKey, knownUpdateValue));
// we are updating the target meta data so also modifying the base
// software
// module so opt lock
// revision must be three
changedLockRevisionTarget = targetManagement.get(target.getId()).get();
assertThat(changedLockRevisionTarget.getOptLockRevision()).isEqualTo(3);
assertThat(changedLockRevisionTarget.getLastModifiedAt()).isGreaterThan(0L);
// verify updated meta data contains the updated value
assertThat(updated).isNotNull();
assertThat(updated.getValue()).isEqualTo(knownUpdateValue);
assertThat(updated.getId().getKey()).isEqualTo(knownKey);
assertThat(updated.getTarget().getControllerId()).isEqualTo(target.getControllerId());
assertThat(updated.getTarget().getId()).isEqualTo(target.getId());
}
@Test
@Description("Queries and loads the metadata related to a given target.")
public void findAllTargetMetadataByControllerId() {
// create targets
final Target target1 = testdataFactory.createTarget("target1");
final Target target2 = testdataFactory.createTarget("target2");
for (int index = 0; index < 10; index++) {
insertTargetMetadata("key" + index, target1, "value" + index);
}
for (int index = 0; index < 8; index++) {
insertTargetMetadata("key" + index, target2, "value" + index);
}
final Page<TargetMetadata> metadataOfTarget1 = targetManagement
.findMetaDataByControllerId(new PageRequest(0, 100), target1.getControllerId());
final Page<TargetMetadata> metadataOfTarget2 = targetManagement
.findMetaDataByControllerId(new PageRequest(0, 100), target2.getControllerId());
assertThat(metadataOfTarget1.getNumberOfElements()).isEqualTo(10);
assertThat(metadataOfTarget1.getTotalElements()).isEqualTo(10);
assertThat(metadataOfTarget2.getNumberOfElements()).isEqualTo(8);
assertThat(metadataOfTarget2.getTotalElements()).isEqualTo(8);
}
} }

View File

@@ -37,12 +37,12 @@ public class RSQLDistributionSetFieldTest extends AbstractJpaIntegrationTest {
DistributionSet ds = testdataFactory.createDistributionSet("DS"); DistributionSet ds = testdataFactory.createDistributionSet("DS");
ds = distributionSetManagement.update(entityFactory.distributionSet().update(ds.getId()).description("DS")); ds = distributionSetManagement.update(entityFactory.distributionSet().update(ds.getId()).description("DS"));
createDistributionSetMetadata(ds.getId(), entityFactory.generateMetadata("metaKey", "metaValue")); createDistributionSetMetadata(ds.getId(), entityFactory.generateDsMetadata("metaKey", "metaValue"));
DistributionSet ds2 = testdataFactory.createDistributionSets("NewDS", 3).get(0); DistributionSet ds2 = testdataFactory.createDistributionSets("NewDS", 3).get(0);
ds2 = distributionSetManagement.update(entityFactory.distributionSet().update(ds2.getId()).description("DS%")); ds2 = distributionSetManagement.update(entityFactory.distributionSet().update(ds2.getId()).description("DS%"));
createDistributionSetMetadata(ds2.getId(), entityFactory.generateMetadata("metaKey", "value")); createDistributionSetMetadata(ds2.getId(), entityFactory.generateDsMetadata("metaKey", "value"));
final DistributionSetTag targetTag = distributionSetTagManagement final DistributionSetTag targetTag = distributionSetTagManagement
.create(entityFactory.tag().create().name("Tag1")); .create(entityFactory.tag().create().name("Tag1"));

View File

@@ -41,13 +41,13 @@ public class RSQLDistributionSetMetadataFieldsTest extends AbstractJpaIntegratio
final List<MetaData> metadata = new ArrayList<>(5); final List<MetaData> metadata = new ArrayList<>(5);
for (int i = 0; i < 5; i++) { for (int i = 0; i < 5; i++) {
metadata.add(entityFactory.generateMetadata("" + i, "" + i)); metadata.add(entityFactory.generateDsMetadata("" + i, "" + i));
} }
distributionSetManagement.createMetaData(distributionSetId, metadata); distributionSetManagement.createMetaData(distributionSetId, metadata);
distributionSetManagement.createMetaData(distributionSetId, distributionSetManagement.createMetaData(distributionSetId,
Arrays.asList(entityFactory.generateMetadata("emptyValueTest", null))); Arrays.asList(entityFactory.generateDsMetadata("emptyValueTest", null)));
} }
@Test @Test

View File

@@ -0,0 +1,80 @@
/**
* Copyright (c) 2015 Bosch Software Innovations 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.rsql;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.eclipse.hawkbit.repository.TargetMetadataFields;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.model.MetaData;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetMetadata;
import org.junit.Before;
import org.junit.Test;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.qameta.allure.Story;
@Feature("Component Tests - Repository")
@Story("RSQL filter target metadata")
public class RSQLTargetMetadataFieldsTest extends AbstractJpaIntegrationTest {
private String controllerId;
@Before
public void setupBeforeTest() {
final Target target = testdataFactory.createTarget("target");
controllerId = target.getControllerId();
final List<MetaData> metadata = new ArrayList<>(5);
for (int i = 0; i < 5; i++) {
metadata.add(entityFactory.generateTargetMetadata("" + i, "" + i));
}
targetManagement.createMetaData(controllerId, metadata);
targetManagement.createMetaData(controllerId,
Arrays.asList(entityFactory.generateTargetMetadata("emptyValueTest", null)));
}
@Test
@Description("Test filter target metadata by key")
public void testFilterByParameterKey() {
assertRSQLQuery(TargetMetadataFields.KEY.name() + "==1", 1);
assertRSQLQuery(TargetMetadataFields.KEY.name() + "!=1", 5);
assertRSQLQuery(TargetMetadataFields.KEY.name() + "=in=(1,2)", 2);
assertRSQLQuery(TargetMetadataFields.KEY.name() + "=out=(1,2)", 4);
}
@Test
@Description("Test filter target metadata by value")
public void testFilterByParameterValue() {
assertRSQLQuery(TargetMetadataFields.VALUE.name() + "==''", 1);
assertRSQLQuery(TargetMetadataFields.VALUE.name() + "!=''", 5);
assertRSQLQuery(TargetMetadataFields.VALUE.name() + "==1", 1);
assertRSQLQuery(TargetMetadataFields.VALUE.name() + "!=1", 4);
assertRSQLQuery(TargetMetadataFields.VALUE.name() + "=in=(1,2)", 2);
assertRSQLQuery(TargetMetadataFields.VALUE.name() + "=out=(1,2)", 3);
}
private void assertRSQLQuery(final String rsqlParam, final long expectedEntities) {
final Page<TargetMetadata> findEnitity = targetManagement
.findMetaDataByControllerIdAndRsql(new PageRequest(0, 100), controllerId, rsqlParam);
final long countAllEntities = findEnitity.getTotalElements();
assertThat(findEnitity).isNotNull();
assertThat(countAllEntities).isEqualTo(expectedEntities);
}
}

View File

@@ -10,6 +10,8 @@ package org.eclipse.hawkbit.mgmt.rest.api;
import java.util.List; import java.util.List;
import org.eclipse.hawkbit.mgmt.json.model.MgmtMetadata;
import org.eclipse.hawkbit.mgmt.json.model.MgmtMetadataBodyPut;
import org.eclipse.hawkbit.mgmt.json.model.PagedList; import org.eclipse.hawkbit.mgmt.json.model.PagedList;
import org.eclipse.hawkbit.mgmt.json.model.action.MgmtAction; import org.eclipse.hawkbit.mgmt.json.model.action.MgmtAction;
import org.eclipse.hawkbit.mgmt.json.model.action.MgmtActionRequestBodyPut; import org.eclipse.hawkbit.mgmt.json.model.action.MgmtActionRequestBodyPut;
@@ -37,13 +39,13 @@ public interface MgmtTargetRestApi {
/** /**
* Handles the GET request of retrieving a single target. * Handles the GET request of retrieving a single target.
* *
* @param controllerId * @param targetId
* the ID of the target to retrieve * the ID of the target to retrieve
* @return a single target with status OK. * @return a single target with status OK.
*/ */
@RequestMapping(method = RequestMethod.GET, value = "/{controllerId}", produces = { MediaTypes.HAL_JSON_VALUE, @RequestMapping(method = RequestMethod.GET, value = "/{targetId}", produces = { MediaTypes.HAL_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE }) MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<MgmtTarget> getTarget(@PathVariable("controllerId") String controllerId); ResponseEntity<MgmtTarget> getTarget(@PathVariable("targetId") String targetId);
/** /**
* Handles the GET request of retrieving all targets. * Handles the GET request of retrieving all targets.
@@ -94,7 +96,7 @@ public interface MgmtTargetRestApi {
* path of the request. A given ID in the request body is ignored. It's not * path of the request. A given ID in the request body is ignored. It's not
* possible to set fields to {@code null} values. * possible to set fields to {@code null} values.
* *
* @param controllerId * @param targetId
* the path parameter which contains the ID of the target * the path parameter which contains the ID of the target
* @param targetRest * @param targetRest
* the request body which contains the fields which should be * the request body which contains the fields which should be
@@ -103,40 +105,40 @@ public interface MgmtTargetRestApi {
* @return the updated target response which contains all fields also fields * @return the updated target response which contains all fields also fields
* which have not updated * which have not updated
*/ */
@RequestMapping(method = RequestMethod.PUT, value = "/{controllerId}", consumes = { MediaTypes.HAL_JSON_VALUE, @RequestMapping(method = RequestMethod.PUT, value = "/{targetId}", consumes = { MediaTypes.HAL_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE }, produces = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE }, produces = { MediaTypes.HAL_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE }) MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<MgmtTarget> updateTarget(@PathVariable("controllerId") String controllerId, ResponseEntity<MgmtTarget> updateTarget(@PathVariable("targetId") String targetId,
MgmtTargetRequestBody targetRest); MgmtTargetRequestBody targetRest);
/** /**
* Handles the DELETE request of deleting a target. * Handles the DELETE request of deleting a target.
* *
* @param controllerId * @param targetId
* the ID of the target to be deleted * the ID of the target to be deleted
* @return If the given controllerId could exists and could be deleted Http * @return If the given targetId could exists and could be deleted Http OK.
* OK. In any failure the JsonResponseExceptionHandler is handling * In any failure the JsonResponseExceptionHandler is handling the
* the response. * response.
*/ */
@RequestMapping(method = RequestMethod.DELETE, value = "/{controllerId}") @RequestMapping(method = RequestMethod.DELETE, value = "/{targetId}")
ResponseEntity<Void> deleteTarget(@PathVariable("controllerId") String controllerId); ResponseEntity<Void> deleteTarget(@PathVariable("targetId") String targetId);
/** /**
* Handles the GET request of retrieving the attributes of a specific * Handles the GET request of retrieving the attributes of a specific
* target. * target.
* *
* @param controllerId * @param targetId
* the ID of the target to retrieve the attributes. * the ID of the target to retrieve the attributes.
* @return the target attributes as map response with status OK * @return the target attributes as map response with status OK
*/ */
@RequestMapping(method = RequestMethod.GET, value = "/{controllerId}/attributes", produces = { @RequestMapping(method = RequestMethod.GET, value = "/{targetId}/attributes", produces = {
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE }) MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<MgmtTargetAttributes> getAttributes(@PathVariable("controllerId") String controllerId); ResponseEntity<MgmtTargetAttributes> getAttributes(@PathVariable("targetId") String targetId);
/** /**
* Handles the GET request of retrieving the Actions of a specific target. * Handles the GET request of retrieving the Actions of a specific target.
* *
* @param controllerId * @param targetId
* to load actions for * to load actions for
* @param pagingOffsetParam * @param pagingOffsetParam
* the offset of list of targets for pagination, might not be * the offset of list of targets for pagination, might not be
@@ -154,9 +156,9 @@ public interface MgmtTargetRestApi {
* status OK. The response is always paged. In any failure the * status OK. The response is always paged. In any failure the
* JsonResponseExceptionHandler is handling the response. * JsonResponseExceptionHandler is handling the response.
*/ */
@RequestMapping(method = RequestMethod.GET, value = "/{controllerId}/actions", produces = { @RequestMapping(method = RequestMethod.GET, value = "/{targetId}/actions", produces = {
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE }) MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<PagedList<MgmtAction>> getActionHistory(@PathVariable("controllerId") String controllerId, ResponseEntity<PagedList<MgmtAction>> getActionHistory(@PathVariable("targetId") String targetId,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) int pagingOffsetParam, @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_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_SORTING, required = false) String sortParam,
@@ -166,22 +168,22 @@ public interface MgmtTargetRestApi {
* Handles the GET request of retrieving a specific Actions of a specific * Handles the GET request of retrieving a specific Actions of a specific
* Target. * Target.
* *
* @param controllerId * @param targetId
* to load the action for * to load the action for
* @param actionId * @param actionId
* to load * to load
* @return the action * @return the action
*/ */
@RequestMapping(method = RequestMethod.GET, value = "/{controllerId}/actions/{actionId}", produces = { @RequestMapping(method = RequestMethod.GET, value = "/{targetId}/actions/{actionId}", produces = {
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE }) MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<MgmtAction> getAction(@PathVariable("controllerId") String controllerId, ResponseEntity<MgmtAction> getAction(@PathVariable("targetId") String targetId,
@PathVariable("actionId") Long actionId); @PathVariable("actionId") Long actionId);
/** /**
* Handles the DELETE request of canceling an specific Actions of a specific * Handles the DELETE request of canceling an specific Actions of a specific
* Target. * Target.
* *
* @param controllerId * @param targetId
* the ID of the target in the URL path parameter * the ID of the target in the URL path parameter
* @param actionId * @param actionId
* the ID of the action in the URL path parameter * the ID of the action in the URL path parameter
@@ -189,15 +191,15 @@ public interface MgmtTargetRestApi {
* optional parameter, which indicates a force cancel * optional parameter, which indicates a force cancel
* @return status no content in case cancellation was successful * @return status no content in case cancellation was successful
*/ */
@RequestMapping(method = RequestMethod.DELETE, value = "/{controllerId}/actions/{actionId}") @RequestMapping(method = RequestMethod.DELETE, value = "/{targetId}/actions/{actionId}")
ResponseEntity<Void> cancelAction(@PathVariable("controllerId") String controllerId, ResponseEntity<Void> cancelAction(@PathVariable("targetId") String targetId,
@PathVariable("actionId") Long actionId, @PathVariable("actionId") Long actionId,
@RequestParam(value = "force", required = false, defaultValue = "false") boolean force); @RequestParam(value = "force", required = false, defaultValue = "false") boolean force);
/** /**
* Handles the PUT update request to switch an action from soft to forced. * Handles the PUT update request to switch an action from soft to forced.
* *
* @param controllerId * @param targetId
* the ID of the target in the URL path parameter * the ID of the target in the URL path parameter
* @param actionId * @param actionId
* the ID of the action in the URL path parameter * the ID of the action in the URL path parameter
@@ -205,17 +207,17 @@ public interface MgmtTargetRestApi {
* to update the action * to update the action
* @return status no content in case cancellation was successful * @return status no content in case cancellation was successful
*/ */
@RequestMapping(method = RequestMethod.PUT, value = "/{controllerId}/actions/{actionId}", consumes = { @RequestMapping(method = RequestMethod.PUT, value = "/{targetId}/actions/{actionId}", consumes = {
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE }, produces = { MediaTypes.HAL_JSON_VALUE, MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE }, produces = { MediaTypes.HAL_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE }) MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<MgmtAction> updateAction(@PathVariable("controllerId") String controllerId, ResponseEntity<MgmtAction> updateAction(@PathVariable("targetId") String targetId,
@PathVariable("actionId") Long actionId, MgmtActionRequestBodyPut actionUpdate); @PathVariable("actionId") Long actionId, MgmtActionRequestBodyPut actionUpdate);
/** /**
* Handles the GET request of retrieving the ActionStatus of a specific * Handles the GET request of retrieving the ActionStatus of a specific
* target and action. * target and action.
* *
* @param controllerId * @param targetId
* of the the action * of the the action
* @param actionId * @param actionId
* of the status we are intend to load * of the status we are intend to load
@@ -232,9 +234,9 @@ public interface MgmtTargetRestApi {
* with status OK. The response is always paged. In any failure the * with status OK. The response is always paged. In any failure the
* JsonResponseExceptionHandler is handling the response. * JsonResponseExceptionHandler is handling the response.
*/ */
@RequestMapping(method = RequestMethod.GET, value = "/{controllerId}/actions/{actionId}/status", produces = { @RequestMapping(method = RequestMethod.GET, value = "/{targetId}/actions/{actionId}/status", produces = {
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE }) MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<PagedList<MgmtActionStatus>> getActionStatusList(@PathVariable("controllerId") String controllerId, ResponseEntity<PagedList<MgmtActionStatus>> getActionStatusList(@PathVariable("targetId") String targetId,
@PathVariable("actionId") Long actionId, @PathVariable("actionId") Long actionId,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) int pagingOffsetParam, @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_PAGING_LIMIT, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) int pagingLimitParam,
@@ -244,20 +246,20 @@ public interface MgmtTargetRestApi {
* Handles the GET request of retrieving the assigned distribution set of an * Handles the GET request of retrieving the assigned distribution set of an
* specific target. * specific target.
* *
* @param controllerId * @param targetId
* the ID of the target to retrieve the assigned distribution * the ID of the target to retrieve the assigned distribution
* *
* @return the assigned distribution set with status OK, if none is assigned * @return the assigned distribution set with status OK, if none is assigned
* than {@code null} content (e.g. "{}") * than {@code null} content (e.g. "{}")
*/ */
@RequestMapping(method = RequestMethod.GET, value = "/{controllerId}/assignedDS", produces = { @RequestMapping(method = RequestMethod.GET, value = "/{targetId}/assignedDS", produces = {
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE }) MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<MgmtDistributionSet> getAssignedDistributionSet(@PathVariable("controllerId") String controllerId); ResponseEntity<MgmtDistributionSet> getAssignedDistributionSet(@PathVariable("targetId") String targetId);
/** /**
* Changes the assigned distribution set of a target. * Changes the assigned distribution set of a target.
* *
* @param controllerId * @param targetId
* of the target to change * of the target to change
* @param dsId * @param dsId
* of the distributionset that is to be assigned * of the distributionset that is to be assigned
@@ -269,24 +271,113 @@ public interface MgmtTargetRestApi {
* complex return body which contains information about the assigned * complex return body which contains information about the assigned
* targets and the already assigned targets counters * targets and the already assigned targets counters
*/ */
@RequestMapping(method = RequestMethod.POST, value = "/{controllerId}/assignedDS", consumes = { @RequestMapping(method = RequestMethod.POST, value = "/{targetId}/assignedDS", consumes = {
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE }, produces = { MediaTypes.HAL_JSON_VALUE, MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE }, produces = { MediaTypes.HAL_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE }) MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<MgmtTargetAssignmentResponseBody> postAssignedDistributionSet( ResponseEntity<MgmtTargetAssignmentResponseBody> postAssignedDistributionSet(
@PathVariable("controllerId") String controllerId, MgmtDistributionSetAssignment dsId, @PathVariable("targetId") String targetId, MgmtDistributionSetAssignment dsId,
@RequestParam(value = "offline", required = false) boolean offline); @RequestParam(value = "offline", required = false) boolean offline);
/** /**
* Handles the GET request of retrieving the installed distribution set of * Handles the GET request of retrieving the installed distribution set of
* an specific target. * an specific target.
* *
* @param controllerId * @param targetId
* the ID of the target to retrieve * the ID of the target to retrieve
* @return the assigned installed set with status OK, if none is installed * @return the assigned installed set with status OK, if none is installed
* than {@code null} content (e.g. "{}") * than {@code null} content (e.g. "{}")
*/ */
@RequestMapping(method = RequestMethod.GET, value = "/{controllerId}/installedDS", produces = { @RequestMapping(method = RequestMethod.GET, value = "/{targetId}/installedDS", produces = {
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE }) MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<MgmtDistributionSet> getInstalledDistributionSet(@PathVariable("controllerId") String controllerId); ResponseEntity<MgmtDistributionSet> getInstalledDistributionSet(@PathVariable("targetId") String targetId);
/**
* Gets a paged list of meta data for a target.
*
* @param targetId
* the ID of the target for the meta data
* @param pagingOffsetParam
* the offset of list of targets for pagination, might not be
* present in the rest request then default value will be applied
* @param pagingLimitParam
* the limit of the paged request, might not be present in the
* rest request then default value will be applied
* @param sortParam
* the sorting parameter in the request URL, syntax
* {@code field:direction, field:direction}
* @param rsqlParam
* the search parameter in the request URL, syntax
* {@code q=key==abc}
* @return status OK if get request is successful with the paged list of
* meta data
*/
@RequestMapping(method = RequestMethod.GET, value = "/{targetId}/metadata", produces = {
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<PagedList<MgmtMetadata>> getMetadata(@PathVariable("targetId") String targetId,
@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);
/**
* Gets a single meta data value for a specific key of a target.
*
* @param targetId
* the ID of the target to get the meta data from
* @param metadataKey
* the key of the meta data entry to retrieve the value from
* @return status OK if get request is successful with the value of the meta
* data
*/
@RequestMapping(method = RequestMethod.GET, value = "/{targetId}/metadata/{metadataKey}", produces = {
MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<MgmtMetadata> getMetadataValue(@PathVariable("targetId") String targetId,
@PathVariable("metadataKey") String metadataKey);
/**
* Updates a single meta data value of a target.
*
* @param targetId
* the ID of the target to update the meta data entry
* @param metadataKey
* the key of the meta data to update the value
* @param metadata
* update body
* @return status OK if the update request is successful and the updated
* meta data result
*/
@RequestMapping(method = RequestMethod.PUT, value = "/{targetId}/metadata/{metadataKey}", produces = {
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<MgmtMetadata> updateMetadata(@PathVariable("targetId") String targetId,
@PathVariable("metadataKey") String metadataKey, MgmtMetadataBodyPut metadata);
/**
* Deletes a single meta data entry from the target.
*
* @param targetId
* the ID of the target to delete the meta data entry
* @param metadataKey
* the key of the meta data to delete
* @return status OK if the delete request is successful
*/
@RequestMapping(method = RequestMethod.DELETE, value = "/{targetId}/metadata/{metadataKey}")
ResponseEntity<Void> deleteMetadata(@PathVariable("targetId") String targetId,
@PathVariable("metadataKey") String metadataKey);
/**
* Creates a list of meta data for a specific target.
*
* @param targetId
* the ID of the targetId to create meta data for
* @param metadataRest
* the list of meta data entries to create
* @return status created if post request is successful with the value of
* the created meta data
*/
@RequestMapping(method = RequestMethod.POST, value = "/{targetId}/metadata", consumes = {
MediaType.APPLICATION_JSON_VALUE,
MediaTypes.HAL_JSON_VALUE }, produces = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<List<MgmtMetadata>> createMetadata(@PathVariable("targetId") String targetId,
List<MgmtMetadata> metadataRest);
} }

View File

@@ -93,7 +93,7 @@ public final class MgmtDistributionSetMapper {
} }
return metadata.stream() return metadata.stream()
.map(metadataRest -> entityFactory.generateMetadata(metadataRest.getKey(), metadataRest.getValue())) .map(metadataRest -> entityFactory.generateDsMetadata(metadataRest.getKey(), metadataRest.getValue()))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }

View File

@@ -321,7 +321,7 @@ public class MgmtDistributionSetResource implements MgmtDistributionSetRestApi {
// check if distribution set exists otherwise throw exception // check if distribution set exists otherwise throw exception
// immediately // immediately
final DistributionSetMetadata updated = distributionSetManagement.updateMetaData(distributionSetId, final DistributionSetMetadata updated = distributionSetManagement.updateMetaData(distributionSetId,
entityFactory.generateMetadata(metadataKey, metadata.getValue())); entityFactory.generateDsMetadata(metadataKey, metadata.getValue()));
return ResponseEntity.ok(MgmtDistributionSetMapper.toResponseDsMetadata(updated)); return ResponseEntity.ok(MgmtDistributionSetMapper.toResponseDsMetadata(updated));
} }

View File

@@ -20,6 +20,7 @@ import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.eclipse.hawkbit.mgmt.json.model.MgmtMaintenanceWindow; import org.eclipse.hawkbit.mgmt.json.model.MgmtMaintenanceWindow;
import org.eclipse.hawkbit.mgmt.json.model.MgmtMetadata;
import org.eclipse.hawkbit.mgmt.json.model.MgmtPollStatus; import org.eclipse.hawkbit.mgmt.json.model.MgmtPollStatus;
import org.eclipse.hawkbit.mgmt.json.model.action.MgmtAction; import org.eclipse.hawkbit.mgmt.json.model.action.MgmtAction;
import org.eclipse.hawkbit.mgmt.json.model.action.MgmtActionStatus; import org.eclipse.hawkbit.mgmt.json.model.action.MgmtActionStatus;
@@ -36,8 +37,10 @@ import org.eclipse.hawkbit.repository.builder.TargetCreate;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType; import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.ActionStatus; import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.MetaData;
import org.eclipse.hawkbit.repository.model.PollStatus; import org.eclipse.hawkbit.repository.model.PollStatus;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetMetadata;
import org.eclipse.hawkbit.rest.data.ResponseList; import org.eclipse.hawkbit.rest.data.ResponseList;
import org.eclipse.hawkbit.rest.data.SortDirection; import org.eclipse.hawkbit.rest.data.SortDirection;
import org.eclipse.hawkbit.util.IpUtil; import org.eclipse.hawkbit.util.IpUtil;
@@ -71,6 +74,9 @@ public final class MgmtTargetMapper {
MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT_VALUE, MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT_VALUE,
ActionFields.ID.getFieldName() + ":" + SortDirection.DESC, null)) ActionFields.ID.getFieldName() + ":" + SortDirection.DESC, null))
.withRel(MgmtRestConstants.TARGET_V1_ACTIONS).expand()); .withRel(MgmtRestConstants.TARGET_V1_ACTIONS).expand());
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"));
} }
static void addPollStatus(final Target target, final MgmtTarget targetRest) { static void addPollStatus(final Target target, final MgmtTarget targetRest) {
@@ -167,6 +173,17 @@ public final class MgmtTargetMapper {
.address(targetRest.getAddress()); .address(targetRest.getAddress());
} }
static List<MetaData> fromRequestTargetMetadata(final List<MgmtMetadata> metadata,
final EntityFactory entityFactory) {
if (metadata == null) {
return Collections.emptyList();
}
return metadata.stream().map(
metadataRest -> entityFactory.generateTargetMetadata(metadataRest.getKey(), metadataRest.getValue()))
.collect(Collectors.toList());
}
static List<MgmtActionStatus> toActionStatusRestResponse(final Collection<ActionStatus> actionStatus, static List<MgmtActionStatus> toActionStatusRestResponse(final Collection<ActionStatus> actionStatus,
final DeploymentManagement deploymentManagement) { final DeploymentManagement deploymentManagement) {
if (actionStatus == null) { if (actionStatus == null) {
@@ -261,4 +278,15 @@ public final class MgmtTargetMapper {
return result; return result;
} }
static MgmtMetadata toResponseTargetMetadata(final TargetMetadata metadata) {
final MgmtMetadata metadataRest = new MgmtMetadata();
metadataRest.setKey(metadata.getKey());
metadataRest.setValue(metadata.getValue());
return metadataRest;
}
static List<MgmtMetadata> toResponseTargetMetadata(final List<TargetMetadata> metadata) {
return metadata.stream().map(MgmtTargetMapper::toResponseTargetMetadata).collect(Collectors.toList());
}
} }

View File

@@ -16,6 +16,8 @@ import java.util.Map;
import javax.validation.ValidationException; import javax.validation.ValidationException;
import org.eclipse.hawkbit.mgmt.json.model.MgmtMaintenanceWindowRequestBody; import org.eclipse.hawkbit.mgmt.json.model.MgmtMaintenanceWindowRequestBody;
import org.eclipse.hawkbit.mgmt.json.model.MgmtMetadata;
import org.eclipse.hawkbit.mgmt.json.model.MgmtMetadataBodyPut;
import org.eclipse.hawkbit.mgmt.json.model.PagedList; import org.eclipse.hawkbit.mgmt.json.model.PagedList;
import org.eclipse.hawkbit.mgmt.json.model.action.MgmtAction; import org.eclipse.hawkbit.mgmt.json.model.action.MgmtAction;
import org.eclipse.hawkbit.mgmt.json.model.action.MgmtActionRequestBodyPut; import org.eclipse.hawkbit.mgmt.json.model.action.MgmtActionRequestBodyPut;
@@ -38,6 +40,7 @@ import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.ActionStatus; import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetMetadata;
import org.eclipse.hawkbit.repository.model.TargetWithActionType; import org.eclipse.hawkbit.repository.model.TargetWithActionType;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -70,8 +73,8 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
private EntityFactory entityFactory; private EntityFactory entityFactory;
@Override @Override
public ResponseEntity<MgmtTarget> getTarget(@PathVariable("controllerId") final String controllerId) { public ResponseEntity<MgmtTarget> getTarget(@PathVariable("targetId") final String targetId) {
final Target findTarget = findTargetWithExceptionIfNotFound(controllerId); final Target findTarget = findTargetWithExceptionIfNotFound(targetId);
// to single response include poll status // to single response include poll status
final MgmtTarget response = MgmtTargetMapper.toResponse(findTarget); final MgmtTarget response = MgmtTargetMapper.toResponse(findTarget);
MgmtTargetMapper.addPollStatus(findTarget, response); MgmtTargetMapper.addPollStatus(findTarget, response);
@@ -117,22 +120,21 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
} }
@Override @Override
public ResponseEntity<MgmtTarget> updateTarget(@PathVariable("controllerId") final String controllerId, public ResponseEntity<MgmtTarget> updateTarget(@PathVariable("targetId") final String targetId,
@RequestBody final MgmtTargetRequestBody targetRest) { @RequestBody final MgmtTargetRequestBody targetRest) {
if (targetRest.isRequestAttributes() != null) { if (targetRest.isRequestAttributes() != null) {
if (targetRest.isRequestAttributes()) { if (targetRest.isRequestAttributes()) {
targetManagement.requestControllerAttributes(controllerId); targetManagement.requestControllerAttributes(targetId);
} else { } else {
return ResponseEntity.badRequest().build(); return ResponseEntity.badRequest().build();
} }
} }
final Target updateTarget = this.targetManagement.update(entityFactory.target().update(controllerId) final Target updateTarget = this.targetManagement.update(entityFactory.target().update(targetId)
.name(targetRest.getName()).description(targetRest.getDescription()).address(targetRest.getAddress()) .name(targetRest.getName()).description(targetRest.getDescription()).address(targetRest.getAddress())
.securityToken(targetRest.getSecurityToken()).requestAttributes(targetRest.isRequestAttributes())); .securityToken(targetRest.getSecurityToken()).requestAttributes(targetRest.isRequestAttributes()));
final MgmtTarget response = MgmtTargetMapper.toResponse(updateTarget); final MgmtTarget response = MgmtTargetMapper.toResponse(updateTarget);
MgmtTargetMapper.addPollStatus(updateTarget, response); MgmtTargetMapper.addPollStatus(updateTarget, response);
MgmtTargetMapper.addTargetLinks(response); MgmtTargetMapper.addTargetLinks(response);
@@ -141,15 +143,15 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
} }
@Override @Override
public ResponseEntity<Void> deleteTarget(@PathVariable("controllerId") final String controllerId) { public ResponseEntity<Void> deleteTarget(@PathVariable("targetId") final String targetId) {
this.targetManagement.deleteByControllerID(controllerId); this.targetManagement.deleteByControllerID(targetId);
LOG.debug("{} target deleted, return status {}", controllerId, HttpStatus.OK); LOG.debug("{} target deleted, return status {}", targetId, HttpStatus.OK);
return ResponseEntity.ok().build(); return ResponseEntity.ok().build();
} }
@Override @Override
public ResponseEntity<MgmtTargetAttributes> getAttributes(@PathVariable("controllerId") final String controllerId) { public ResponseEntity<MgmtTargetAttributes> getAttributes(@PathVariable("targetId") final String targetId) {
final Map<String, String> controllerAttributes = targetManagement.getControllerAttributes(controllerId); final Map<String, String> controllerAttributes = targetManagement.getControllerAttributes(targetId);
if (controllerAttributes.isEmpty()) { if (controllerAttributes.isEmpty()) {
return ResponseEntity.noContent().build(); return ResponseEntity.noContent().build();
} }
@@ -162,13 +164,13 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
@Override @Override
public ResponseEntity<PagedList<MgmtAction>> getActionHistory( public ResponseEntity<PagedList<MgmtAction>> getActionHistory(
@PathVariable("controllerId") final String controllerId, @PathVariable("targetId") final String targetId,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) final int pagingOffsetParam, @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_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_SORTING, required = false) final String sortParam,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) final String rsqlParam) { @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) final String rsqlParam) {
findTargetWithExceptionIfNotFound(controllerId); findTargetWithExceptionIfNotFound(targetId);
final int sanitizedOffsetParam = PagingUtility.sanitizeOffsetParam(pagingOffsetParam); final int sanitizedOffsetParam = PagingUtility.sanitizeOffsetParam(pagingOffsetParam);
final int sanitizedLimitParam = PagingUtility.sanitizePageLimitParam(pagingLimitParam); final int sanitizedLimitParam = PagingUtility.sanitizePageLimitParam(pagingLimitParam);
@@ -178,40 +180,41 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
final Slice<Action> activeActions; final Slice<Action> activeActions;
final Long totalActionCount; final Long totalActionCount;
if (rsqlParam != null) { if (rsqlParam != null) {
activeActions = this.deploymentManagement.findActionsByTarget(rsqlParam, controllerId, pageable); activeActions = this.deploymentManagement.findActionsByTarget(rsqlParam, targetId, pageable);
totalActionCount = this.deploymentManagement.countActionsByTarget(rsqlParam, controllerId); totalActionCount = this.deploymentManagement.countActionsByTarget(rsqlParam, targetId);
} else { } else {
activeActions = this.deploymentManagement.findActionsByTarget(controllerId, pageable); activeActions = this.deploymentManagement.findActionsByTarget(targetId, pageable);
totalActionCount = this.deploymentManagement.countActionsByTarget(controllerId); totalActionCount = this.deploymentManagement.countActionsByTarget(targetId);
} }
return ResponseEntity.ok(new PagedList<>(MgmtTargetMapper.toResponse(controllerId, activeActions.getContent()), return ResponseEntity.ok(
new PagedList<>(MgmtTargetMapper.toResponse(targetId, activeActions.getContent()),
totalActionCount)); totalActionCount));
} }
@Override @Override
public ResponseEntity<MgmtAction> getAction(@PathVariable("controllerId") final String controllerId, public ResponseEntity<MgmtAction> getAction(@PathVariable("targetId") final String targetId,
@PathVariable("actionId") final Long actionId) { @PathVariable("actionId") final Long actionId) {
final Action action = deploymentManagement.findAction(actionId) final Action action = deploymentManagement.findAction(actionId)
.orElseThrow(() -> new EntityNotFoundException(Action.class, actionId)); .orElseThrow(() -> new EntityNotFoundException(Action.class, actionId));
if (!action.getTarget().getControllerId().equals(controllerId)) { if (!action.getTarget().getControllerId().equals(targetId)) {
LOG.warn("given action ({}) is not assigned to given target ({}).", action.getId(), controllerId); LOG.warn("given action ({}) is not assigned to given target ({}).", action.getId(), targetId);
return ResponseEntity.notFound().build(); return ResponseEntity.notFound().build();
} }
return ResponseEntity.ok(MgmtTargetMapper.toResponseWithLinks(controllerId, action)); return ResponseEntity.ok(MgmtTargetMapper.toResponseWithLinks(targetId, action));
} }
@Override @Override
public ResponseEntity<Void> cancelAction(@PathVariable("controllerId") final String controllerId, public ResponseEntity<Void> cancelAction(@PathVariable("targetId") final String targetId,
@PathVariable("actionId") final Long actionId, @PathVariable("actionId") final Long actionId,
@RequestParam(value = "force", required = false, defaultValue = "false") final boolean force) { @RequestParam(value = "force", required = false, defaultValue = "false") final boolean force) {
final Action action = deploymentManagement.findAction(actionId) final Action action = deploymentManagement.findAction(actionId)
.orElseThrow(() -> new EntityNotFoundException(Action.class, actionId)); .orElseThrow(() -> new EntityNotFoundException(Action.class, actionId));
if (!action.getTarget().getControllerId().equals(controllerId)) { if (!action.getTarget().getControllerId().equals(targetId)) {
LOG.warn("given action ({}) is not assigned to given target ({}).", actionId, controllerId); LOG.warn("given action ({}) is not assigned to given target ({}).", actionId, targetId);
return ResponseEntity.notFound().build(); return ResponseEntity.notFound().build();
} }
@@ -228,12 +231,12 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
@Override @Override
public ResponseEntity<PagedList<MgmtActionStatus>> getActionStatusList( public ResponseEntity<PagedList<MgmtActionStatus>> getActionStatusList(
@PathVariable("controllerId") final String controllerId, @PathVariable("actionId") final Long actionId, @PathVariable("targetId") final String targetId, @PathVariable("actionId") final Long actionId,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) final int pagingOffsetParam, @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_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_SORTING, required = false) final String sortParam) {
final Target target = findTargetWithExceptionIfNotFound(controllerId); final Target target = findTargetWithExceptionIfNotFound(targetId);
final Action action = deploymentManagement.findAction(actionId) final Action action = deploymentManagement.findAction(actionId)
.orElseThrow(() -> new EntityNotFoundException(Action.class, actionId)); .orElseThrow(() -> new EntityNotFoundException(Action.class, actionId));
@@ -258,8 +261,8 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
@Override @Override
public ResponseEntity<MgmtDistributionSet> getAssignedDistributionSet( public ResponseEntity<MgmtDistributionSet> getAssignedDistributionSet(
@PathVariable("controllerId") final String controllerId) { @PathVariable("targetId") final String targetId) {
final MgmtDistributionSet distributionSetRest = deploymentManagement.getAssignedDistributionSet(controllerId) final MgmtDistributionSet distributionSetRest = deploymentManagement.getAssignedDistributionSet(targetId)
.map(ds -> { .map(ds -> {
final MgmtDistributionSet response = MgmtDistributionSetMapper.toResponse(ds); final MgmtDistributionSet response = MgmtDistributionSetMapper.toResponse(ds);
MgmtDistributionSetMapper.addLinks(ds, response); MgmtDistributionSetMapper.addLinks(ds, response);
@@ -275,21 +278,21 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
@Override @Override
public ResponseEntity<MgmtTargetAssignmentResponseBody> postAssignedDistributionSet( public ResponseEntity<MgmtTargetAssignmentResponseBody> postAssignedDistributionSet(
@PathVariable("controllerId") final String controllerId, @PathVariable("targetId") final String targetId,
@RequestBody final MgmtDistributionSetAssignment dsId, @RequestBody final MgmtDistributionSetAssignment dsId,
@RequestParam(value = "offline", required = false) final boolean offline) { @RequestParam(value = "offline", required = false) final boolean offline) {
if (offline) { if (offline) {
return ResponseEntity.ok(MgmtDistributionSetMapper.toResponse( return ResponseEntity.ok(MgmtDistributionSetMapper.toResponse(
deploymentManagement.offlineAssignedDistributionSet(dsId.getId(), Arrays.asList(controllerId)))); deploymentManagement.offlineAssignedDistributionSet(dsId.getId(), Arrays.asList(targetId))));
} }
findTargetWithExceptionIfNotFound(controllerId); findTargetWithExceptionIfNotFound(targetId);
final MgmtMaintenanceWindowRequestBody maintenanceWindow = dsId.getMaintenanceWindow(); final MgmtMaintenanceWindowRequestBody maintenanceWindow = dsId.getMaintenanceWindow();
if (maintenanceWindow == null) { if (maintenanceWindow == null) {
return ResponseEntity.ok(MgmtDistributionSetMapper.toResponse(this.deploymentManagement return ResponseEntity.ok(MgmtDistributionSetMapper.toResponse(this.deploymentManagement
.assignDistributionSet(dsId.getId(), Arrays.asList(new TargetWithActionType(controllerId, .assignDistributionSet(dsId.getId(), Arrays.asList(new TargetWithActionType(targetId,
MgmtRestModelMapper.convertActionType(dsId.getType()), dsId.getForcetime()))))); MgmtRestModelMapper.convertActionType(dsId.getType()), dsId.getForcetime())))));
} }
@@ -301,7 +304,7 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
return ResponseEntity return ResponseEntity
.ok(MgmtDistributionSetMapper.toResponse(this.deploymentManagement.assignDistributionSet(dsId.getId(), .ok(MgmtDistributionSetMapper.toResponse(this.deploymentManagement.assignDistributionSet(dsId.getId(),
Arrays.asList(new TargetWithActionType(controllerId, Arrays.asList(new TargetWithActionType(targetId,
MgmtRestModelMapper.convertActionType(dsId.getType()), dsId.getForcetime(), MgmtRestModelMapper.convertActionType(dsId.getType()), dsId.getForcetime(),
cronSchedule, duration, timezone))))); cronSchedule, duration, timezone)))));
@@ -309,8 +312,8 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
@Override @Override
public ResponseEntity<MgmtDistributionSet> getInstalledDistributionSet( public ResponseEntity<MgmtDistributionSet> getInstalledDistributionSet(
@PathVariable("controllerId") final String controllerId) { @PathVariable("targetId") final String targetId) {
final MgmtDistributionSet distributionSetRest = deploymentManagement.getInstalledDistributionSet(controllerId) final MgmtDistributionSet distributionSetRest = deploymentManagement.getInstalledDistributionSet(targetId)
.map(set -> { .map(set -> {
final MgmtDistributionSet response = MgmtDistributionSetMapper.toResponse(set); final MgmtDistributionSet response = MgmtDistributionSetMapper.toResponse(set);
MgmtDistributionSetMapper.addLinks(set, response); MgmtDistributionSetMapper.addLinks(set, response);
@@ -325,19 +328,19 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
return ResponseEntity.ok(distributionSetRest); return ResponseEntity.ok(distributionSetRest);
} }
private Target findTargetWithExceptionIfNotFound(final String controllerId) { private Target findTargetWithExceptionIfNotFound(final String targetId) {
return targetManagement.getByControllerID(controllerId) return targetManagement.getByControllerID(targetId)
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId)); .orElseThrow(() -> new EntityNotFoundException(Target.class, targetId));
} }
@Override @Override
public ResponseEntity<MgmtAction> updateAction(@PathVariable("controllerId") final String controllerId, public ResponseEntity<MgmtAction> updateAction(@PathVariable("targetId") final String targetId,
@PathVariable("actionId") final Long actionId, @RequestBody final MgmtActionRequestBodyPut actionUpdate) { @PathVariable("actionId") final Long actionId, @RequestBody final MgmtActionRequestBodyPut actionUpdate) {
Action action = deploymentManagement.findAction(actionId) Action action = deploymentManagement.findAction(actionId)
.orElseThrow(() -> new EntityNotFoundException(Action.class, actionId)); .orElseThrow(() -> new EntityNotFoundException(Action.class, actionId));
if (!action.getTarget().getControllerId().equals(controllerId)) { if (!action.getTarget().getControllerId().equals(targetId)) {
LOG.warn("given action ({}) is not assigned to given target ({}).", action.getId(), controllerId); LOG.warn("given action ({}) is not assigned to given target ({}).", action.getId(), targetId);
return ResponseEntity.notFound().build(); return ResponseEntity.notFound().build();
} }
@@ -347,7 +350,62 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
action = deploymentManagement.forceTargetAction(actionId); action = deploymentManagement.forceTargetAction(actionId);
return ResponseEntity.ok(MgmtTargetMapper.toResponseWithLinks(controllerId, action)); return ResponseEntity.ok(MgmtTargetMapper.toResponseWithLinks(targetId, action));
}
@Override
public ResponseEntity<PagedList<MgmtMetadata>> getMetadata(@PathVariable("targetId") final String targetId,
@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.sanitizeDistributionSetMetadataSortParam(sortParam);
final Pageable pageable = new OffsetBasedPageRequest(sanitizedOffsetParam, sanitizedLimitParam, sorting);
final Page<TargetMetadata> metaDataPage;
if (rsqlParam != null) {
metaDataPage = targetManagement.findMetaDataByControllerIdAndRsql(pageable, targetId, rsqlParam);
} else {
metaDataPage = targetManagement.findMetaDataByControllerId(pageable, targetId);
}
return ResponseEntity.ok(new PagedList<>(MgmtTargetMapper.toResponseTargetMetadata(metaDataPage.getContent()),
metaDataPage.getTotalElements()));
}
@Override
public ResponseEntity<MgmtMetadata> getMetadataValue(@PathVariable("targetId") final String targetId,
@PathVariable("metadataKey") final String metadataKey) {
final TargetMetadata findOne = targetManagement.getMetaDataByControllerId(targetId, metadataKey)
.orElseThrow(() -> new EntityNotFoundException(TargetMetadata.class, targetId, metadataKey));
return ResponseEntity.ok(MgmtTargetMapper.toResponseTargetMetadata(findOne));
}
@Override
public ResponseEntity<MgmtMetadata> updateMetadata(@PathVariable("targetId") final String targetId,
@PathVariable("metadataKey") final String metadataKey, @RequestBody final MgmtMetadataBodyPut metadata) {
final TargetMetadata updated = targetManagement.updateMetaData(targetId,
entityFactory.generateTargetMetadata(metadataKey, metadata.getValue()));
return ResponseEntity.ok(MgmtTargetMapper.toResponseTargetMetadata(updated));
}
@Override
public ResponseEntity<Void> deleteMetadata(@PathVariable("targetId") final String targetId,
@PathVariable("metadataKey") final String metadataKey) {
targetManagement.deleteMetaData(targetId, metadataKey);
return ResponseEntity.ok().build();
}
@Override
public ResponseEntity<List<MgmtMetadata>> createMetadata(@PathVariable("targetId") final String targetId,
@RequestBody final List<MgmtMetadata> metadataRest) {
final List<TargetMetadata> created = targetManagement.createMetaData(targetId,
MgmtTargetMapper.fromRequestTargetMetadata(metadataRest, entityFactory));
return new ResponseEntity<>(MgmtTargetMapper.toResponseTargetMetadata(created), HttpStatus.CREATED);
} }
} }

View File

@@ -1036,7 +1036,7 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
final String updateValue = "valueForUpdate"; final String updateValue = "valueForUpdate";
final DistributionSet testDS = testdataFactory.createDistributionSet("one"); final DistributionSet testDS = testdataFactory.createDistributionSet("one");
createDistributionSetMetadata(testDS.getId(), entityFactory.generateMetadata(knownKey, knownValue)); createDistributionSetMetadata(testDS.getId(), entityFactory.generateDsMetadata(knownKey, knownValue));
final JSONObject jsonObject = new JSONObject().put("key", knownKey).put("value", updateValue); final JSONObject jsonObject = new JSONObject().put("key", knownKey).put("value", updateValue);
@@ -1060,7 +1060,7 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
final String knownValue = "knownValue"; final String knownValue = "knownValue";
final DistributionSet testDS = testdataFactory.createDistributionSet("one"); final DistributionSet testDS = testdataFactory.createDistributionSet("one");
createDistributionSetMetadata(testDS.getId(), entityFactory.generateMetadata(knownKey, knownValue)); createDistributionSetMetadata(testDS.getId(), entityFactory.generateDsMetadata(knownKey, knownValue));
mvc.perform(delete("/rest/v1/distributionsets/{dsId}/metadata/{key}", testDS.getId(), knownKey)) mvc.perform(delete("/rest/v1/distributionsets/{dsId}/metadata/{key}", testDS.getId(), knownKey))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()); .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
@@ -1076,7 +1076,7 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
final String knownValue = "knownValue"; final String knownValue = "knownValue";
final DistributionSet testDS = testdataFactory.createDistributionSet("one"); final DistributionSet testDS = testdataFactory.createDistributionSet("one");
createDistributionSetMetadata(testDS.getId(), entityFactory.generateMetadata(knownKey, knownValue)); createDistributionSetMetadata(testDS.getId(), entityFactory.generateDsMetadata(knownKey, knownValue));
mvc.perform(delete("/rest/v1/distributionsets/{dsId}/metadata/XXX", testDS.getId(), knownKey)) mvc.perform(delete("/rest/v1/distributionsets/{dsId}/metadata/XXX", testDS.getId(), knownKey))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound()); .andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
@@ -1094,7 +1094,7 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
final String knownKey = "knownKey"; final String knownKey = "knownKey";
final String knownValue = "knownValue"; final String knownValue = "knownValue";
final DistributionSet testDS = testdataFactory.createDistributionSet("one"); final DistributionSet testDS = testdataFactory.createDistributionSet("one");
createDistributionSetMetadata(testDS.getId(), entityFactory.generateMetadata(knownKey, knownValue)); createDistributionSetMetadata(testDS.getId(), entityFactory.generateDsMetadata(knownKey, knownValue));
mvc.perform(get("/rest/v1/distributionsets/{dsId}/metadata/{key}", testDS.getId(), knownKey)) mvc.perform(get("/rest/v1/distributionsets/{dsId}/metadata/{key}", testDS.getId(), knownKey))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
@@ -1113,7 +1113,7 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
final DistributionSet testDS = testdataFactory.createDistributionSet("one"); final DistributionSet testDS = testdataFactory.createDistributionSet("one");
for (int index = 0; index < totalMetadata; index++) { for (int index = 0; index < totalMetadata; index++) {
createDistributionSetMetadata(testDS.getId(), createDistributionSetMetadata(testDS.getId(),
entityFactory.generateMetadata(knownKeyPrefix + index, knownValuePrefix + index)); entityFactory.generateDsMetadata(knownKeyPrefix + index, knownValuePrefix + index));
} }
mvc.perform(get("/rest/v1/distributionsets/{dsId}/metadata?offset=" + offsetParam + "&limit=" + limitParam, mvc.perform(get("/rest/v1/distributionsets/{dsId}/metadata?offset=" + offsetParam + "&limit=" + limitParam,
@@ -1189,7 +1189,7 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
final DistributionSet testDS = testdataFactory.createDistributionSet("one"); final DistributionSet testDS = testdataFactory.createDistributionSet("one");
for (int index = 0; index < totalMetadata; index++) { for (int index = 0; index < totalMetadata; index++) {
createDistributionSetMetadata(testDS.getId(), createDistributionSetMetadata(testDS.getId(),
entityFactory.generateMetadata(knownKeyPrefix + index, knownValuePrefix + index)); entityFactory.generateDsMetadata(knownKeyPrefix + index, knownValuePrefix + index));
} }
final String rsqlSearchValue1 = "value==knownValue1"; final String rsqlSearchValue1 = "value==knownValue1";

View File

@@ -26,8 +26,10 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -45,8 +47,10 @@ import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.Action.Status; import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.ActionStatus; import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.MetaData;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetMetadata;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.test.util.WithUser; import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.eclipse.hawkbit.rest.exception.MessageNotReadableException; import org.eclipse.hawkbit.rest.exception.MessageNotReadableException;
@@ -54,6 +58,7 @@ import org.eclipse.hawkbit.rest.json.model.ExceptionInfo;
import org.eclipse.hawkbit.rest.util.JsonBuilder; import org.eclipse.hawkbit.rest.util.JsonBuilder;
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter; import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
import org.eclipse.hawkbit.util.IpUtil; import org.eclipse.hawkbit.util.IpUtil;
import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
import org.junit.Test; import org.junit.Test;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
@@ -1564,8 +1569,8 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
final String body = new JSONObject().put("requestAttributes", true).toString(); final String body = new JSONObject().put("requestAttributes", true).toString();
mvc.perform(put(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId).content(body) mvc.perform(put(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId).content(body)
.contentType(MediaType.APPLICATION_JSON)) .contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()); .andExpect(status().isOk());
assertThat(targetManagement.isControllerAttributesRequested(knownTargetId)).isTrue(); assertThat(targetManagement.isControllerAttributesRequested(knownTargetId)).isTrue();
} }
@@ -1575,8 +1580,8 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
final String body = new JSONObject().put("description", "verify attribute can be missing").toString(); final String body = new JSONObject().put("description", "verify attribute can be missing").toString();
mvc.perform(put(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId).content(body) mvc.perform(put(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId).content(body)
.contentType(MediaType.APPLICATION_JSON)) .contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()); .andExpect(status().isOk());
} }
@Step @Step
@@ -1584,8 +1589,8 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
final String body = new JSONObject().put("requestAttributes", false).toString(); final String body = new JSONObject().put("requestAttributes", false).toString();
mvc.perform(put(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId).content(body) mvc.perform(put(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId).content(body)
.contentType(MediaType.APPLICATION_JSON)) .contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest()); .andExpect(status().isBadRequest());
assertThat(targetManagement.isControllerAttributesRequested(knownTargetId)).isTrue(); assertThat(targetManagement.isControllerAttributesRequested(knownTargetId)).isTrue();
} }
@@ -1649,4 +1654,193 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
assertThat(actionsByTarget.getContent()).hasSize(1); assertThat(actionsByTarget.getContent()).hasSize(1);
return targetManagement.getByControllerID(tA.getControllerId()).get(); return targetManagement.getByControllerID(tA.getControllerId()).get();
} }
@Test
@Description("Ensures that the metadata creation through API is reflected by the repository.")
public void createMetadata() throws Exception {
final String knownControllerId = "targetIdWithMetadata";
testdataFactory.createTarget(knownControllerId);
final String knownKey1 = "knownKey1";
final String knownKey2 = "knownKey2";
final String knownValue1 = "knownValue1";
final String knownValue2 = "knownValue2";
final JSONArray metaData1 = new JSONArray();
metaData1.put(new JSONObject().put("key", knownKey1).put("value", knownValue1));
metaData1.put(new JSONObject().put("key", knownKey2).put("value", knownValue2));
mvc.perform(
post("/rest/v1/targets/{targetId}/metadata", knownControllerId).accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON).content(metaData1.toString()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isCreated())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andExpect(jsonPath("[0]key", equalTo(knownKey1))).andExpect(jsonPath("[0]value", equalTo(knownValue1)))
.andExpect(jsonPath("[1]key", equalTo(knownKey2)))
.andExpect(jsonPath("[1]value", equalTo(knownValue2)));
final TargetMetadata metaKey1 = targetManagement.getMetaDataByControllerId(knownControllerId, knownKey1).get();
final TargetMetadata metaKey2 = targetManagement.getMetaDataByControllerId(knownControllerId, knownKey2).get();
assertThat(metaKey1.getValue()).isEqualTo(knownValue1);
assertThat(metaKey2.getValue()).isEqualTo(knownValue2);
// verify quota enforcement
final int maxMetaData = quotaManagement.getMaxMetaDataEntriesPerTarget();
final JSONArray metaData2 = new JSONArray();
for (int i = 0; i < maxMetaData - metaData1.length() + 1; ++i) {
metaData2.put(new JSONObject().put("key", knownKey1 + i).put("value", knownValue1 + i));
}
mvc.perform(
post("/rest/v1/targets/{targetId}/metadata", knownControllerId).accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON).content(metaData2.toString()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isForbidden());
// verify that the number of meta data entries has not changed
// (we cannot use the PAGE constant here as it tries to sort by ID)
assertThat(targetManagement.findMetaDataByControllerId(new PageRequest(0, Integer.MAX_VALUE), knownControllerId)
.getTotalElements()).isEqualTo(metaData1.length());
}
@Test
@Description("Ensures that a metadata update through API is reflected by the repository.")
public void updateMetadata() throws Exception {
final String knownControllerId = "targetIdWithMetadata";
// prepare and create metadata for update
final String knownKey = "knownKey";
final String knownValue = "knownValue";
final String updateValue = "valueForUpdate";
setupTargetWithMetadata(knownControllerId, knownKey, knownValue);
final JSONObject jsonObject = new JSONObject().put("key", knownKey).put("value", updateValue);
mvc.perform(put("/rest/v1/targets/{targetId}/metadata/{key}", knownControllerId, knownKey)
.accept(MediaType.APPLICATION_JSON).contentType(MediaType.APPLICATION_JSON)
.content(jsonObject.toString())).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andExpect(jsonPath("key", equalTo(knownKey))).andExpect(jsonPath("value", equalTo(updateValue)));
final TargetMetadata updatedTargetMetadata = targetManagement
.getMetaDataByControllerId(knownControllerId, knownKey).get();
assertThat(updatedTargetMetadata.getValue()).isEqualTo(updateValue);
}
private void setupTargetWithMetadata(final String knownControllerId, final String knownKey,
final String knownValue) {
testdataFactory.createTarget(knownControllerId);
targetManagement.createMetaData(knownControllerId,
Collections.singletonList(entityFactory.generateTargetMetadata(knownKey, knownValue)));
}
@Test
@Description("Ensures that a metadata entry deletion through API is reflected by the repository.")
public void deleteMetadata() throws Exception {
final String knownControllerId = "targetIdWithMetadata";
// prepare and create metadata for deletion
final String knownKey = "knownKey";
final String knownValue = "knownValue";
setupTargetWithMetadata(knownControllerId, knownKey, knownValue);
mvc.perform(delete("/rest/v1/targets/{targetId}/metadata/{key}", knownControllerId, knownKey))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
assertThat(targetManagement.getMetaDataByControllerId(knownControllerId, knownKey)).isNotPresent();
}
@Test
@Description("Ensures that target metadata deletion request to API on an entity that does not exist results in NOT_FOUND.")
public void deleteMetadataThatDoesNotExistLeadsToNotFound() throws Exception {
final String knownControllerId = "targetIdWithMetadata";
// prepare and create metadata for deletion
final String knownKey = "knownKey";
final String knownValue = "knownValue";
setupTargetWithMetadata(knownControllerId, knownKey, knownValue);
mvc.perform(delete("/rest/v1/targets/{targetId}/metadata/XXX", knownControllerId))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
mvc.perform(delete("/rest/v1/targets/1234/metadata/{key}", knownKey)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isNotFound());
assertThat(targetManagement.getMetaDataByControllerId(knownControllerId, knownKey)).isPresent();
}
@Test
@Description("Ensures that a metadata entry selection through API reflectes the repository content.")
public void getSingleMetadata() throws Exception {
final String knownControllerId = "targetIdWithMetadata";
// prepare and create metadata for deletion
final String knownKey = "knownKey";
final String knownValue = "knownValue";
setupTargetWithMetadata(knownControllerId, knownKey, knownValue);
mvc.perform(get("/rest/v1/targets/{targetId}/metadata/{key}", knownControllerId, knownKey))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("key", equalTo(knownKey))).andExpect(jsonPath("value", equalTo(knownValue)));
}
@Test
@Description("Ensures that a metadata entry paged list selection through API reflectes the repository content.")
public void getPagedListOfMetadata() throws Exception {
final String knownControllerId = "targetIdWithMetadata";
final int totalMetadata = 10;
final int limitParam = 5;
final String offsetParam = "0";
final String knownKeyPrefix = "knownKey";
final String knownValuePrefix = "knownValue";
setupTargetWithMetadata(knownControllerId, knownKeyPrefix, knownValuePrefix, totalMetadata);
mvc.perform(get("/rest/v1/targets/{targetId}/metadata?offset=" + offsetParam + "&limit=" + limitParam,
knownControllerId)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("size", equalTo(limitParam))).andExpect(jsonPath("total", equalTo(totalMetadata)))
.andExpect(jsonPath("content[0].key", equalTo("knownKey0")))
.andExpect(jsonPath("content[0].value", equalTo("knownValue0")));
}
private void setupTargetWithMetadata(final String knownControllerId, final String knownKeyPrefix,
final String knownValuePrefix, final int totalMetadata) {
testdataFactory.createTarget(knownControllerId);
final List<MetaData> targetMetadataEntries = new LinkedList<>();
for (int index = 0; index < totalMetadata; index++) {
targetMetadataEntries
.add(entityFactory.generateTargetMetadata(knownKeyPrefix + index, knownValuePrefix + index));
}
targetManagement.createMetaData(knownControllerId, targetMetadataEntries);
}
@Test
@Description("Ensures that a target metadata filtered query with value==knownValue1 parameter returns only the metadata entries with that value.")
public void searchDistributionSetMetadataRsql() throws Exception {
final String knownControllerId = "targetIdWithMetadata";
final int totalMetadata = 10;
final String knownKeyPrefix = "knownKey";
final String knownValuePrefix = "knownValue";
setupTargetWithMetadata(knownControllerId, knownKeyPrefix, knownValuePrefix, totalMetadata);
final String rsqlSearchValue1 = "value==knownValue1";
mvc.perform(get("/rest/v1/targets/{targetId}/metadata?q=" + rsqlSearchValue1, knownControllerId))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()).andExpect(jsonPath("size", equalTo(1)))
.andExpect(jsonPath("total", equalTo(1))).andExpect(jsonPath("content[0].key", equalTo("knownKey1")))
.andExpect(jsonPath("content[0].value", equalTo("knownValue1")));
}
} }

View File

@@ -683,6 +683,241 @@ include::../errors/406.adoc[]
include::../errors/429.adoc[] include::../errors/429.adoc[]
|=== |===
== GET /rest/v1/targets/{targetId}/metadata
=== Implementation Notes
Get a paged list of meta data for a target. Required permission: READ_REPOSITORY
=== Get a paged list of meta data
==== Curl
include::{snippets}/targets/get-metadata/curl-request.adoc[]
==== Request URL
include::{snippets}/targets/get-metadata/http-request.adoc[]
==== Request path parameter
include::{snippets}/targets/get-metadata/path-parameters.adoc[]
==== Request query parameter
include::{snippets}/targets/get-metadata-with-parameters/request-parameters.adoc[]
==== Request parameter example
include::{snippets}/targets/get-metadata-with-parameters/http-request.adoc[]
=== Response (Status 200)
==== Response fields
include::{snippets}/targets/get-metadata/response-fields.adoc[]
==== Response example
include::{snippets}/targets/get-metadata/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/targets/{targetId}/metadata
=== Implementation Notes
Create a list of meta data entries Required permissions: READ_REPOSITORY and UPDATE_TARGET
=== Create a list of meta data entries
==== CURL
include::{snippets}/targets/create-metadata/curl-request.adoc[]
==== Request URL
include::{snippets}/targets/create-metadata/http-request.adoc[]
==== Request path parameter
include::{snippets}/targets/create-metadata/path-parameters.adoc[]
==== Request fields
include::{snippets}/targets/create-metadata/request-fields.adoc[]
=== Response (Status 200)
==== Response example
include::{snippets}/targets/create-metadata/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/404.adoc[]
include::../errors/405.adoc[]
include::../errors/406.adoc[]
include::../errors/409.adoc[]
include::../errors/415.adoc[]
include::../errors/429.adoc[]
|===
== DELETE /rest/v1/targets/{targetId}/metadata/{metadataKey}
=== Implementation Notes
Delete a single meta data. Required permission: UPDATE_REPOSITORY
=== Delete a single meta data
==== CURL
include::{snippets}/targets/delete-metadata/curl-request.adoc[]
==== Request URL
include::{snippets}/targets/delete-metadata/http-request.adoc[]
==== Request path parameter
include::{snippets}/targets/delete-metadata/path-parameters.adoc[]
=== Response (Status 200)
==== Response example
include::{snippets}/targets/delete-metadata/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/404.adoc[]
include::../errors/405.adoc[]
include::../errors/406.adoc[]
include::../errors/429.adoc[]
|===
== GET /rest/v1/targets/{targetId}/metadata/{metadataKey}
=== Implementation Notes
Get a single meta data value for a meta data key. Required permission: READ_REPOSITORY
=== Get a single meta data value
==== Curl
include::{snippets}/targets/get-metadata-value/curl-request.adoc[]
==== Request URL
include::{snippets}/targets/get-metadata-value/http-request.adoc[]
==== Request path parameter
include::{snippets}/targets/get-metadata-value/path-parameters.adoc[]
=== Response (Status 200)
==== Response fields
include::{snippets}/targets/get-metadata-value/response-fields.adoc[]
==== Response example
include::{snippets}/targets/get-metadata-value/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[]
|===
== PUT /rest/v1/targets/{targetId}/metadata/{metadataKey}
=== Implementation Notes
Update a single meta data value for speficic key. Required permission: UPDATE_REPOSITORY
=== Update a single meta data value
==== Curl
include::{snippets}/targets/update-metadata/curl-request.adoc[]
==== Request URL
include::{snippets}/targets/update-metadata/http-request.adoc[]
==== Request path parameter
include::{snippets}/targets/update-metadata/path-parameters.adoc[]
==== Request fields
include::{snippets}/targets/update-metadata/request-fields.adoc[]
=== Response (Status 200)
==== Response fields
include::{snippets}/targets/update-metadata/response-fields.adoc[]
==== Response example
include::{snippets}/targets/update-metadata/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/404.adoc[]
include::../errors/405.adoc[]
include::../errors/406.adoc[]
include::../errors/409.adoc[]
include::../errors/415.adoc[]
include::../errors/429.adoc[]
|===
== Additional content == Additional content

View File

@@ -248,7 +248,9 @@ public abstract class AbstractApiRestDocumentation extends AbstractRestIntegrati
fieldWithPath(fieldArrayPrefix + "_links.attributes") fieldWithPath(fieldArrayPrefix + "_links.attributes")
.description(MgmtApiModelProperties.LINKS_ATTRIBUTES), .description(MgmtApiModelProperties.LINKS_ATTRIBUTES),
fieldWithPath(fieldArrayPrefix + "_links.actions") fieldWithPath(fieldArrayPrefix + "_links.actions")
.description(MgmtApiModelProperties.LINKS_ACTIONS))); .description(MgmtApiModelProperties.LINKS_ACTIONS),
fieldWithPath(fieldArrayPrefix + "_links.metadata").description(MgmtApiModelProperties.META_DATA)));
} }
fields.addAll(Arrays.asList(descriptors)); fields.addAll(Arrays.asList(descriptors));

View File

@@ -504,7 +504,7 @@ public class DistributionSetsDocumentationTest extends AbstractApiRestDocumentat
final DistributionSet testDS = testdataFactory.createDistributionSet("one"); final DistributionSet testDS = testdataFactory.createDistributionSet("one");
for (int index = 0; index < totalMetadata; index++) { for (int index = 0; index < totalMetadata; index++) {
distributionSetManagement.createMetaData(testDS.getId(), Lists distributionSetManagement.createMetaData(testDS.getId(), Lists
.newArrayList(entityFactory.generateMetadata(knownKeyPrefix + index, knownValuePrefix + index))); .newArrayList(entityFactory.generateDsMetadata(knownKeyPrefix + index, knownValuePrefix + index)));
} }
mockMvc.perform(get(MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING + "/{distributionSetId}/metadata", mockMvc.perform(get(MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING + "/{distributionSetId}/metadata",
@@ -532,7 +532,7 @@ public class DistributionSetsDocumentationTest extends AbstractApiRestDocumentat
final DistributionSet testDS = testdataFactory.createDistributionSet("one"); final DistributionSet testDS = testdataFactory.createDistributionSet("one");
for (int index = 0; index < totalMetadata; index++) { for (int index = 0; index < totalMetadata; index++) {
distributionSetManagement.createMetaData(testDS.getId(), Lists distributionSetManagement.createMetaData(testDS.getId(), Lists
.newArrayList(entityFactory.generateMetadata(knownKeyPrefix + index, knownValuePrefix + index))); .newArrayList(entityFactory.generateDsMetadata(knownKeyPrefix + index, knownValuePrefix + index)));
} }
mockMvc.perform(get(MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING + "/{dsId}/metadata", testDS.getId()) mockMvc.perform(get(MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING + "/{dsId}/metadata", testDS.getId())
@@ -564,7 +564,7 @@ public class DistributionSetsDocumentationTest extends AbstractApiRestDocumentat
final String knownValue = "knownValue"; final String knownValue = "knownValue";
final DistributionSet testDS = testdataFactory.createDistributionSet("one"); final DistributionSet testDS = testdataFactory.createDistributionSet("one");
distributionSetManagement.createMetaData(testDS.getId(), distributionSetManagement.createMetaData(testDS.getId(),
Arrays.asList(entityFactory.generateMetadata(knownKey, knownValue))); Arrays.asList(entityFactory.generateDsMetadata(knownKey, knownValue)));
mockMvc.perform(get( mockMvc.perform(get(
MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING + "/{distributionSetId}/metadata/{metadatakey}", MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING + "/{distributionSetId}/metadata/{metadatakey}",
@@ -588,7 +588,7 @@ public class DistributionSetsDocumentationTest extends AbstractApiRestDocumentat
final DistributionSet testDS = testdataFactory.createDistributionSet("one"); final DistributionSet testDS = testdataFactory.createDistributionSet("one");
distributionSetManagement.createMetaData(testDS.getId(), distributionSetManagement.createMetaData(testDS.getId(),
Arrays.asList(entityFactory.generateMetadata(knownKey, knownValue))); Arrays.asList(entityFactory.generateDsMetadata(knownKey, knownValue)));
final JSONObject jsonObject = new JSONObject().put("key", knownKey).put("value", updateValue); final JSONObject jsonObject = new JSONObject().put("key", knownKey).put("value", updateValue);
@@ -616,7 +616,7 @@ public class DistributionSetsDocumentationTest extends AbstractApiRestDocumentat
final DistributionSet testDS = testdataFactory.createDistributionSet("one"); final DistributionSet testDS = testdataFactory.createDistributionSet("one");
distributionSetManagement.createMetaData(testDS.getId(), distributionSetManagement.createMetaData(testDS.getId(),
Arrays.asList(entityFactory.generateMetadata(knownKey, knownValue))); Arrays.asList(entityFactory.generateDsMetadata(knownKey, knownValue)));
mockMvc.perform( mockMvc.perform(
delete(MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING + "/{distributionSetId}/metadata/{key}", delete(MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING + "/{distributionSetId}/metadata/{key}",

View File

@@ -20,6 +20,7 @@ import static org.springframework.restdocs.request.RequestDocumentation.paramete
import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; import static org.springframework.restdocs.request.RequestDocumentation.pathParameters;
import static org.springframework.restdocs.request.RequestDocumentation.requestParameters; import static org.springframework.restdocs.request.RequestDocumentation.requestParameters;
import static org.springframework.restdocs.snippet.Attributes.key; import static org.springframework.restdocs.snippet.Attributes.key;
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.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -28,6 +29,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.repository.ActionStatusFields; import org.eclipse.hawkbit.repository.ActionStatusFields;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
@@ -38,6 +40,7 @@ import org.eclipse.hawkbit.rest.documentation.AbstractApiRestDocumentation;
import org.eclipse.hawkbit.rest.documentation.ApiModelPropertiesGeneric; import org.eclipse.hawkbit.rest.documentation.ApiModelPropertiesGeneric;
import org.eclipse.hawkbit.rest.documentation.MgmtApiModelProperties; import org.eclipse.hawkbit.rest.documentation.MgmtApiModelProperties;
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter; import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -47,6 +50,7 @@ import org.springframework.http.MediaType;
import org.springframework.restdocs.payload.JsonFieldType; import org.springframework.restdocs.payload.JsonFieldType;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.common.collect.Lists;
import io.qameta.allure.Description; import io.qameta.allure.Description;
import io.qameta.allure.Feature; import io.qameta.allure.Feature;
@@ -60,7 +64,7 @@ import io.qameta.allure.Story;
@Story("Target Resource") @Story("Target Resource")
public class TargetResourceDocumentationTest extends AbstractApiRestDocumentation { public class TargetResourceDocumentationTest extends AbstractApiRestDocumentation {
private final String controllerId = "137"; private final String targetId = "137";
@Override @Override
@Before @Before
@@ -72,7 +76,7 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
@Test @Test
@Description("Handles the GET request of retrieving all targets within SP. Required Permission: READ_TARGET.") @Description("Handles the GET request of retrieving all targets within SP. Required Permission: READ_TARGET.")
public void getTargets() throws Exception { public void getTargets() throws Exception {
createTargetByGivenNameWithAttributes(controllerId, createDistributionSet()); createTargetByGivenNameWithAttributes(targetId, createDistributionSet());
mockMvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING)).andExpect(status().isOk()) mockMvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING)).andExpect(status().isOk())
.andDo(MockMvcResultPrinter.print()) .andDo(MockMvcResultPrinter.print())
@@ -90,7 +94,8 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
.type("enum").attributes( .type("enum").attributes(
key("value").value("['error', 'in_sync', 'pending', 'registered', 'unknown']")), key("value").value("['error', 'in_sync', 'pending', 'registered', 'unknown']")),
fieldWithPath("content[].securityToken").description(MgmtApiModelProperties.SECURITY_TOKEN), fieldWithPath("content[].securityToken").description(MgmtApiModelProperties.SECURITY_TOKEN),
fieldWithPath("content[].requestAttributes").description(MgmtApiModelProperties.REQUEST_ATTRIBUTES), fieldWithPath("content[].requestAttributes")
.description(MgmtApiModelProperties.REQUEST_ATTRIBUTES),
fieldWithPath("content[].installedAt").description(MgmtApiModelProperties.INSTALLED_AT), fieldWithPath("content[].installedAt").description(MgmtApiModelProperties.INSTALLED_AT),
fieldWithPath("content[].lastModifiedAt") fieldWithPath("content[].lastModifiedAt")
.description(ApiModelPropertiesGeneric.LAST_MODIFIED_AT).type("Number"), .description(ApiModelPropertiesGeneric.LAST_MODIFIED_AT).type("Number"),
@@ -142,67 +147,71 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
.attributes(key("value") .attributes(key("value")
.value("['error', 'in_sync', 'pending', 'registered', 'unknown']")), .value("['error', 'in_sync', 'pending', 'registered', 'unknown']")),
fieldWithPath("[]securityToken").description(MgmtApiModelProperties.SECURITY_TOKEN), fieldWithPath("[]securityToken").description(MgmtApiModelProperties.SECURITY_TOKEN),
fieldWithPath("[]requestAttributes").description(MgmtApiModelProperties.REQUEST_ATTRIBUTES), fieldWithPath("[]requestAttributes")
.description(MgmtApiModelProperties.REQUEST_ATTRIBUTES),
fieldWithPath("[]_links.self").ignored()))); fieldWithPath("[]_links.self").ignored())));
} }
@Test @Test
@Description("Handles the DELETE request of deleting a single target within SP. Required Permission: DELETE_TARGET.") @Description("Handles the DELETE request of deleting a single target within SP. Required Permission: DELETE_TARGET.")
public void deleteTarget() throws Exception { public void deleteTarget() throws Exception {
final Target target = testdataFactory.createTarget(controllerId); final Target target = testdataFactory.createTarget(targetId);
mockMvc.perform( mockMvc.perform(
delete(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{controllerId}", target.getControllerId())) delete(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}", target.getControllerId()))
.andExpect(status().isOk()).andDo(MockMvcResultPrinter.print()).andDo(this.document.document( .andExpect(status().isOk()).andDo(MockMvcResultPrinter.print()).andDo(this.document.document(
pathParameters(parameterWithName("controllerId").description(ApiModelPropertiesGeneric.NAME)))); pathParameters(parameterWithName("targetId").description(ApiModelPropertiesGeneric.ITEM_ID))));
} }
@Test @Test
@Description("Handles the GET request of retrieving a single target within SP. Required Permission: READ_TARGET.") @Description("Handles the GET request of retrieving a single target within SP. Required Permission: READ_TARGET.")
public void getTarget() throws Exception { public void getTarget() throws Exception {
final Target target = createTargetByGivenNameWithAttributes(controllerId, createDistributionSet()); final Target target = createTargetByGivenNameWithAttributes(targetId, createDistributionSet());
mockMvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{controllerId}", target.getControllerId())) mockMvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}", target.getControllerId()))
.andExpect(status().isOk()).andDo(MockMvcResultPrinter.print()) .andExpect(status().isOk()).andDo(MockMvcResultPrinter.print())
.andDo(this.document.document( .andDo(this.document.document(
pathParameters( pathParameters(
parameterWithName("controllerId").description(ApiModelPropertiesGeneric.ITEM_ID)), parameterWithName("targetId").description(ApiModelPropertiesGeneric.ITEM_ID)),
getResponseFieldTarget(false))); getResponseFieldTarget(false)));
} }
@Test @Test
@Description("Handles the PUT request of updating a target within SP. Required Permission: UPDATE_TARGET.") @Description("Handles the PUT request of updating a target within SP. Required Permission: UPDATE_TARGET.")
public void putTarget() throws Exception { public void putTarget() throws Exception {
final Target target = createTargetByGivenNameWithAttributes(controllerId, createDistributionSet()); final Target target = createTargetByGivenNameWithAttributes(targetId, createDistributionSet());
final String targetAsJson = createJsonTarget(controllerId, "newTargetName", "I've been updated"); final String targetAsJson = createJsonTarget(targetId, "newTargetName", "I've been updated");
mockMvc.perform(put(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{controllerId}", target.getControllerId()) mockMvc.perform(put(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}", target.getControllerId())
.contentType(MediaType.APPLICATION_JSON_UTF8).content(targetAsJson)).andExpect(status().isOk()) .contentType(MediaType.APPLICATION_JSON_UTF8).content(targetAsJson)).andExpect(status().isOk())
.andDo(MockMvcResultPrinter.print()) .andDo(MockMvcResultPrinter.print())
.andDo(this.document.document( .andDo(this.document.document(
pathParameters( pathParameters(
parameterWithName("controllerId").description(ApiModelPropertiesGeneric.ITEM_ID)), parameterWithName("targetId").description(ApiModelPropertiesGeneric.ITEM_ID)),
requestFields(optionalRequestFieldWithPath("name").description(ApiModelPropertiesGeneric.NAME), requestFields(optionalRequestFieldWithPath("name").description(ApiModelPropertiesGeneric.NAME),
optionalRequestFieldWithPath("description").description(ApiModelPropertiesGeneric.DESCRPTION), optionalRequestFieldWithPath("description")
optionalRequestFieldWithPath("controllerId").description(ApiModelPropertiesGeneric.ITEM_ID), .description(ApiModelPropertiesGeneric.DESCRPTION),
optionalRequestFieldWithPath("controllerId")
.description(ApiModelPropertiesGeneric.ITEM_ID),
optionalRequestFieldWithPath("address").description(MgmtApiModelProperties.ADDRESS), optionalRequestFieldWithPath("address").description(MgmtApiModelProperties.ADDRESS),
optionalRequestFieldWithPath("securityToken") optionalRequestFieldWithPath("securityToken")
.description(MgmtApiModelProperties.SECURITY_TOKEN), .description(MgmtApiModelProperties.SECURITY_TOKEN),
optionalRequestFieldWithPath("requestAttributes").description(MgmtApiModelProperties.REQUEST_ATTRIBUTES)), optionalRequestFieldWithPath("requestAttributes")
.description(MgmtApiModelProperties.REQUEST_ATTRIBUTES)),
getResponseFieldTarget(false))); getResponseFieldTarget(false)));
} }
@Test @Test
@Description("Handles the GET request of retrieving the full action history of a specific target. Required Permission: READ_TARGET.") @Description("Handles the GET request of retrieving the full action history of a specific target. Required Permission: READ_TARGET.")
public void getActionsFromTarget() throws Exception { public void getActionsFromTarget() throws Exception {
generateActionForTarget(controllerId); generateActionForTarget(targetId);
mockMvc.perform(get( mockMvc.perform(get(
MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{controllerId}/" + MgmtRestConstants.TARGET_V1_ACTIONS, MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/" + MgmtRestConstants.TARGET_V1_ACTIONS,
controllerId)).andExpect(status().isOk()).andDo(MockMvcResultPrinter.print()) targetId)).andExpect(status().isOk()).andDo(MockMvcResultPrinter.print())
.andDo(this.document.document( .andDo(this.document.document(
pathParameters( pathParameters(
parameterWithName("controllerId").description(ApiModelPropertiesGeneric.ITEM_ID)), parameterWithName("targetId").description(ApiModelPropertiesGeneric.ITEM_ID)),
responseFields( responseFields(
fieldWithPath("size").type(JsonFieldType.NUMBER) fieldWithPath("size").type(JsonFieldType.NUMBER)
.description(ApiModelPropertiesGeneric.SIZE), .description(ApiModelPropertiesGeneric.SIZE),
@@ -227,14 +236,14 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
@Test @Test
@Description("Handles the GET request of retrieving the full action history of a specific target with maintenance window. Required Permission: READ_TARGET.") @Description("Handles the GET request of retrieving the full action history of a specific target with maintenance window. Required Permission: READ_TARGET.")
public void getActionsFromTargetWithMaintenanceWindow() throws Exception { public void getActionsFromTargetWithMaintenanceWindow() throws Exception {
generateActionForTarget(controllerId, true, false, getTestSchedule(2), getTestDuration(1), getTestTimeZone()); generateActionForTarget(targetId, true, false, getTestSchedule(2), getTestDuration(1), getTestTimeZone());
mockMvc.perform(get( mockMvc.perform(get(
MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{controllerId}/" + MgmtRestConstants.TARGET_V1_ACTIONS, MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/" + MgmtRestConstants.TARGET_V1_ACTIONS,
controllerId)).andExpect(status().isOk()).andDo(MockMvcResultPrinter.print()) targetId)).andExpect(status().isOk()).andDo(MockMvcResultPrinter.print())
.andDo(this.document.document( .andDo(this.document.document(
pathParameters( pathParameters(
parameterWithName("controllerId").description(ApiModelPropertiesGeneric.ITEM_ID)), parameterWithName("targetId").description(ApiModelPropertiesGeneric.ITEM_ID)),
responseFields( responseFields(
fieldWithPath("size").type(JsonFieldType.NUMBER) fieldWithPath("size").type(JsonFieldType.NUMBER)
.description(ApiModelPropertiesGeneric.SIZE), .description(ApiModelPropertiesGeneric.SIZE),
@@ -269,9 +278,9 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
@Test @Test
@Description("Handles the GET request of retrieving all targets within SP based by parameter. Required Permission: READ_TARGET.") @Description("Handles the GET request of retrieving all targets within SP based by parameter. Required Permission: READ_TARGET.")
public void getActionsFromTargetWithParameters() throws Exception { public void getActionsFromTargetWithParameters() throws Exception {
generateActionForTarget(controllerId); generateActionForTarget(targetId);
mockMvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + controllerId + "/" mockMvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + targetId + "/"
+ MgmtRestConstants.TARGET_V1_ACTIONS + "?limit=10&sort=id:ASC&offset=0&q=status==pending")) + MgmtRestConstants.TARGET_V1_ACTIONS + "?limit=10&sort=id:ASC&offset=0&q=status==pending"))
.andExpect(status().isOk()).andDo(MockMvcResultPrinter.print()) .andExpect(status().isOk()).andDo(MockMvcResultPrinter.print())
.andDo(this.document.document(requestParameters( .andDo(this.document.document(requestParameters(
@@ -285,22 +294,22 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
@Test @Test
@Description("Cancels an active action, only active actions can be deleted. Required Permission: UPDATE_TARGET.") @Description("Cancels an active action, only active actions can be deleted. Required Permission: UPDATE_TARGET.")
public void deleteActionFromTarget() throws Exception { public void deleteActionFromTarget() throws Exception {
final Action actions = generateActionForTarget(controllerId, false); final Action actions = generateActionForTarget(targetId, false);
mockMvc.perform(delete(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{controllerId}/" mockMvc.perform(delete(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/"
+ MgmtRestConstants.TARGET_V1_ACTIONS + "/{actionId}", controllerId, actions.getId())) + MgmtRestConstants.TARGET_V1_ACTIONS + "/{actionId}", targetId, actions.getId()))
.andExpect(status().isNoContent()).andDo(MockMvcResultPrinter.print()) .andExpect(status().isNoContent()).andDo(MockMvcResultPrinter.print())
.andDo(this.document.document( .andDo(this.document.document(
pathParameters(parameterWithName("controllerId").description(ApiModelPropertiesGeneric.ITEM_ID), pathParameters(parameterWithName("targetId").description(ApiModelPropertiesGeneric.ITEM_ID),
parameterWithName("actionId").description(ApiModelPropertiesGeneric.ITEM_ID)))); parameterWithName("actionId").description(ApiModelPropertiesGeneric.ITEM_ID))));
} }
@Test @Test
@Description("Handles the GET request of retrieving all targets within SP based by parameter. Required Permission: READ_TARGET.") @Description("Handles the GET request of retrieving all targets within SP based by parameter. Required Permission: READ_TARGET.")
public void deleteActionsFromTargetWithParameters() throws Exception { public void deleteActionsFromTargetWithParameters() throws Exception {
generateActionForTarget(controllerId); generateActionForTarget(targetId);
mockMvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + controllerId + "/" mockMvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + targetId + "/"
+ MgmtRestConstants.TARGET_V1_ACTIONS + "?force=true")).andExpect(status().isOk()) + MgmtRestConstants.TARGET_V1_ACTIONS + "?force=true")).andExpect(status().isOk())
.andDo(MockMvcResultPrinter.print()).andDo(this.document.document( .andDo(MockMvcResultPrinter.print()).andDo(this.document.document(
requestParameters(parameterWithName("force").description(MgmtApiModelProperties.FORCE)))); requestParameters(parameterWithName("force").description(MgmtApiModelProperties.FORCE))));
@@ -309,15 +318,15 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
@Test @Test
@Description("Handles the GET request of retrieving a specific action on a specific target. Required Permission: READ_TARGET.") @Description("Handles the GET request of retrieving a specific action on a specific target. Required Permission: READ_TARGET.")
public void getActionFromTarget() throws Exception { public void getActionFromTarget() throws Exception {
final Action action = generateActionForTarget(controllerId, true, true); final Action action = generateActionForTarget(targetId, true, true);
assertThat(deploymentManagement.findAction(action.getId()).get().getActionType()) assertThat(deploymentManagement.findAction(action.getId()).get().getActionType())
.isEqualTo(ActionType.TIMEFORCED); .isEqualTo(ActionType.TIMEFORCED);
mockMvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{controllerId}/" mockMvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/"
+ MgmtRestConstants.TARGET_V1_ACTIONS + "/{actionId}", controllerId, action.getId())) + MgmtRestConstants.TARGET_V1_ACTIONS + "/{actionId}", targetId, action.getId()))
.andExpect(status().isOk()).andDo(MockMvcResultPrinter.print()) .andExpect(status().isOk()).andDo(MockMvcResultPrinter.print())
.andDo(this.document.document( .andDo(this.document.document(
pathParameters(parameterWithName("controllerId").description(ApiModelPropertiesGeneric.ITEM_ID), pathParameters(parameterWithName("targetId").description(ApiModelPropertiesGeneric.ITEM_ID),
parameterWithName("actionId").description(ApiModelPropertiesGeneric.ITEM_ID)), parameterWithName("actionId").description(ApiModelPropertiesGeneric.ITEM_ID)),
responseFields(fieldWithPath("createdBy").description(ApiModelPropertiesGeneric.CREATED_BY), responseFields(fieldWithPath("createdBy").description(ApiModelPropertiesGeneric.CREATED_BY),
fieldWithPath("createdAt").description(ApiModelPropertiesGeneric.CREATED_AT), fieldWithPath("createdAt").description(ApiModelPropertiesGeneric.CREATED_AT),
@@ -343,14 +352,14 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
@Test @Test
@Description("Handles the GET request of retrieving a specific action on a specific target. Required Permission: READ_TARGET.") @Description("Handles the GET request of retrieving a specific action on a specific target. Required Permission: READ_TARGET.")
public void getActionFromTargetWithMaintenanceWindow() throws Exception { public void getActionFromTargetWithMaintenanceWindow() throws Exception {
final Action action = generateActionForTarget(controllerId, true, true, getTestSchedule(2), getTestDuration(1), final Action action = generateActionForTarget(targetId, true, true, getTestSchedule(2), getTestDuration(1),
getTestTimeZone()); getTestTimeZone());
mockMvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{controllerId}/" mockMvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/"
+ MgmtRestConstants.TARGET_V1_ACTIONS + "/{actionId}", controllerId, action.getId())) + MgmtRestConstants.TARGET_V1_ACTIONS + "/{actionId}", targetId, action.getId()))
.andExpect(status().isOk()).andDo(MockMvcResultPrinter.print()) .andExpect(status().isOk()).andDo(MockMvcResultPrinter.print())
.andDo(this.document.document( .andDo(this.document.document(
pathParameters(parameterWithName("controllerId").description(ApiModelPropertiesGeneric.ITEM_ID), pathParameters(parameterWithName("targetId").description(ApiModelPropertiesGeneric.ITEM_ID),
parameterWithName("actionId").description(ApiModelPropertiesGeneric.ITEM_ID)), parameterWithName("actionId").description(ApiModelPropertiesGeneric.ITEM_ID)),
responseFields(fieldWithPath("createdBy").description(ApiModelPropertiesGeneric.CREATED_BY), responseFields(fieldWithPath("createdBy").description(ApiModelPropertiesGeneric.CREATED_BY),
fieldWithPath("createdAt").description(ApiModelPropertiesGeneric.CREATED_AT), fieldWithPath("createdAt").description(ApiModelPropertiesGeneric.CREATED_AT),
@@ -386,7 +395,7 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
@Test @Test
@Description("Handles the PUT request to switch an action from soft to forced. Required Permission: UPDATE_TARGET.") @Description("Handles the PUT request to switch an action from soft to forced. Required Permission: UPDATE_TARGET.")
public void switchActionToForced() throws Exception { public void switchActionToForced() throws Exception {
final Target target = testdataFactory.createTarget(controllerId); final Target target = testdataFactory.createTarget(targetId);
final DistributionSet set = testdataFactory.createDistributionSet(); final DistributionSet set = testdataFactory.createDistributionSet();
final Long actionId = deploymentManagement final Long actionId = deploymentManagement
.assignDistributionSet(set.getId(), ActionType.SOFT, 0, Arrays.asList(target.getControllerId())) .assignDistributionSet(set.getId(), ActionType.SOFT, 0, Arrays.asList(target.getControllerId()))
@@ -396,13 +405,13 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
final Map<String, Object> body = new HashMap<>(); final Map<String, Object> body = new HashMap<>();
body.put("forceType", "forced"); body.put("forceType", "forced");
mockMvc.perform(put(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{controllerId}/" mockMvc.perform(put(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/"
+ MgmtRestConstants.TARGET_V1_ACTIONS + "/{actionId}", controllerId, actionId) + MgmtRestConstants.TARGET_V1_ACTIONS + "/{actionId}", targetId, actionId)
.content(this.objectMapper.writeValueAsString(body)) .content(this.objectMapper.writeValueAsString(body))
.contentType(MediaType.APPLICATION_JSON_UTF8)) .contentType(MediaType.APPLICATION_JSON_UTF8))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andDo(this.document.document( .andDo(this.document.document(
pathParameters(parameterWithName("controllerId").description(ApiModelPropertiesGeneric.ITEM_ID), pathParameters(parameterWithName("targetId").description(ApiModelPropertiesGeneric.ITEM_ID),
parameterWithName("actionId").description(ApiModelPropertiesGeneric.ITEM_ID)), parameterWithName("actionId").description(ApiModelPropertiesGeneric.ITEM_ID)),
requestFields( requestFields(
requestFieldWithPath("forceType").description(MgmtApiModelProperties.ACTION_FORCED)), requestFieldWithPath("forceType").description(MgmtApiModelProperties.ACTION_FORCED)),
@@ -428,13 +437,13 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
@Test @Test
@Description("Handles the GET request of retrieving a specific action on a specific target. Required Permission: READ_TARGET.") @Description("Handles the GET request of retrieving a specific action on a specific target. Required Permission: READ_TARGET.")
public void getStatusFromAction() throws Exception { public void getStatusFromAction() throws Exception {
final Action action = generateActionForTarget(controllerId); final Action action = generateActionForTarget(targetId);
mockMvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{controllerId}/" mockMvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/"
+ MgmtRestConstants.TARGET_V1_ACTIONS + "/{actionId}/" + MgmtRestConstants.TARGET_V1_ACTION_STATUS, + MgmtRestConstants.TARGET_V1_ACTIONS + "/{actionId}/" + MgmtRestConstants.TARGET_V1_ACTION_STATUS,
controllerId, action.getId())).andExpect(status().isOk()).andDo(MockMvcResultPrinter.print()) targetId, action.getId())).andExpect(status().isOk()).andDo(MockMvcResultPrinter.print())
.andDo(this.document.document( .andDo(this.document.document(
pathParameters(parameterWithName("controllerId").description(ApiModelPropertiesGeneric.ITEM_ID), pathParameters(parameterWithName("targetId").description(ApiModelPropertiesGeneric.ITEM_ID),
parameterWithName("actionId").description(ApiModelPropertiesGeneric.ITEM_ID)), parameterWithName("actionId").description(ApiModelPropertiesGeneric.ITEM_ID)),
responseFields( responseFields(
fieldWithPath("size").type(JsonFieldType.NUMBER) fieldWithPath("size").type(JsonFieldType.NUMBER)
@@ -454,9 +463,9 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
@Test @Test
@Description("Handles the GET request of retrieving all targets within SP based by parameter. Required Permission: READ_TARGET.") @Description("Handles the GET request of retrieving all targets within SP based by parameter. Required Permission: READ_TARGET.")
public void getStatusFromActionWithParameters() throws Exception { public void getStatusFromActionWithParameters() throws Exception {
final Action action = generateActionForTarget(controllerId); final Action action = generateActionForTarget(targetId);
mockMvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + controllerId + "/" mockMvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + targetId + "/"
+ MgmtRestConstants.TARGET_V1_ACTIONS + "/" + action.getId() + "/" + MgmtRestConstants.TARGET_V1_ACTIONS + "/" + action.getId() + "/"
+ MgmtRestConstants.TARGET_V1_ACTION_STATUS + "?limit=10&sort=id:ASC&offset=0")) + MgmtRestConstants.TARGET_V1_ACTION_STATUS + "?limit=10&sort=id:ASC&offset=0"))
.andExpect(status().isOk()).andDo(MockMvcResultPrinter.print()) .andExpect(status().isOk()).andDo(MockMvcResultPrinter.print())
@@ -469,21 +478,21 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
@Test @Test
@Description("Handles the GET request of retrieving the assigned distribution set of an specific target. Required Permission: READ_TARGET.") @Description("Handles the GET request of retrieving the assigned distribution set of an specific target. Required Permission: READ_TARGET.")
public void getAssignedDistributionSetFromAction() throws Exception { public void getAssignedDistributionSetFromAction() throws Exception {
generateActionForTarget(controllerId); generateActionForTarget(targetId);
mockMvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{controllerId}/" mockMvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/"
+ MgmtRestConstants.TARGET_V1_ASSIGNED_DISTRIBUTION_SET, controllerId)).andExpect(status().isOk()) + MgmtRestConstants.TARGET_V1_ASSIGNED_DISTRIBUTION_SET, targetId)).andExpect(status().isOk())
.andDo(MockMvcResultPrinter.print()) .andDo(MockMvcResultPrinter.print())
.andDo(this.document.document( .andDo(this.document.document(
pathParameters( pathParameters(
parameterWithName("controllerId").description(ApiModelPropertiesGeneric.ITEM_ID)), parameterWithName("targetId").description(ApiModelPropertiesGeneric.ITEM_ID)),
getResponseFieldsDistributionSet(false))); getResponseFieldsDistributionSet(false)));
} }
@Test @Test
@Description("Handles the POST request for assigning a distribution set to a specific target. Required Permission: READ_REPOSITORY and UPDATE_TARGET.") @Description("Handles the POST request for assigning a distribution set to a specific target. Required Permission: READ_REPOSITORY and UPDATE_TARGET.")
public void postAssignDistributionSetToTarget() throws Exception { public void postAssignDistributionSetToTarget() throws Exception {
testdataFactory.createTarget(controllerId); testdataFactory.createTarget(targetId);
final DistributionSet set = testdataFactory.createDistributionSet("one"); final DistributionSet set = testdataFactory.createDistributionSet("one");
final long forceTime = System.currentTimeMillis(); final long forceTime = System.currentTimeMillis();
@@ -492,13 +501,13 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
getMaintenanceWindow(getTestSchedule(10), getTestDuration(10), getTestTimeZone())) getMaintenanceWindow(getTestSchedule(10), getTestDuration(10), getTestTimeZone()))
.toString(); .toString();
mockMvc.perform(post(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{controllerId}/" mockMvc.perform(post(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/"
+ MgmtRestConstants.TARGET_V1_ASSIGNED_DISTRIBUTION_SET, controllerId).content(body) + MgmtRestConstants.TARGET_V1_ASSIGNED_DISTRIBUTION_SET, targetId).content(body)
.contentType(MediaType.APPLICATION_JSON_UTF8)) .contentType(MediaType.APPLICATION_JSON_UTF8))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andDo(this.document.document( .andDo(this.document.document(
pathParameters( pathParameters(
parameterWithName("controllerId").description(ApiModelPropertiesGeneric.ITEM_ID)), parameterWithName("targetId").description(ApiModelPropertiesGeneric.ITEM_ID)),
requestParameters(parameterWithName("offline") requestParameters(parameterWithName("offline")
.description(MgmtApiModelProperties.OFFLINE_UPDATE).optional()), .description(MgmtApiModelProperties.OFFLINE_UPDATE).optional()),
requestFields(requestFieldWithPath("forcetime").description(MgmtApiModelProperties.FORCETIME), requestFields(requestFieldWithPath("forcetime").description(MgmtApiModelProperties.FORCETIME),
@@ -528,32 +537,193 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
final Map<String, String> knownControllerAttrs = new HashMap<>(); final Map<String, String> knownControllerAttrs = new HashMap<>();
knownControllerAttrs.put("a", "1"); knownControllerAttrs.put("a", "1");
knownControllerAttrs.put("b", "2"); knownControllerAttrs.put("b", "2");
final Target target = testdataFactory.createTarget(controllerId); final Target target = testdataFactory.createTarget(targetId);
controllerManagement.updateControllerAttributes(controllerId, knownControllerAttrs, null); controllerManagement.updateControllerAttributes(targetId, knownControllerAttrs, null);
// test query target over rest resource // test query target over rest resource
mockMvc.perform( mockMvc.perform(
get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{controllerId}/attributes", target.getName())) get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/attributes", target.getName()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().is2xxSuccessful()) .andDo(MockMvcResultPrinter.print()).andExpect(status().is2xxSuccessful())
.andExpect(jsonPath("$.a", equalTo("1"))).andExpect(jsonPath("$.b", equalTo("2"))) .andExpect(jsonPath("$.a", equalTo("1"))).andExpect(jsonPath("$.b", equalTo("2")))
.andDo(this.document.document(pathParameters( .andDo(this.document.document(pathParameters(
parameterWithName("controllerId").description(ApiModelPropertiesGeneric.ITEM_ID)))); parameterWithName("targetId").description(ApiModelPropertiesGeneric.ITEM_ID))));
} }
@Test @Test
@Description("Handles the GET request of retrieving the installed distribution set of an specific target. Required Permission: READ_TARGET.") @Description("Handles the GET request of retrieving the installed distribution set of an specific target. Required Permission: READ_TARGET.")
public void getInstalledDistributionSetFromTarget() throws Exception { public void getInstalledDistributionSetFromTarget() throws Exception {
final Target target = createTargetByGivenNameWithAttributes(controllerId, createDistributionSet()); final Target target = createTargetByGivenNameWithAttributes(targetId, createDistributionSet());
mockMvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{controllerId}/" mockMvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/"
+ MgmtRestConstants.TARGET_V1_INSTALLED_DISTRIBUTION_SET, target.getName())).andExpect(status().isOk()) + MgmtRestConstants.TARGET_V1_INSTALLED_DISTRIBUTION_SET, target.getName())).andExpect(status().isOk())
.andDo(MockMvcResultPrinter.print()) .andDo(MockMvcResultPrinter.print())
.andDo(this.document.document( .andDo(this.document.document(
pathParameters( pathParameters(
parameterWithName("controllerId").description(ApiModelPropertiesGeneric.ITEM_ID)), parameterWithName("targetId").description(ApiModelPropertiesGeneric.ITEM_ID)),
getResponseFieldsDistributionSet(false))); getResponseFieldsDistributionSet(false)));
} }
@Test
@Description("Get a paged list of meta data for a target with standard page size." + " Required Permission: "
+ SpPermission.READ_REPOSITORY)
public void getMetadata() throws Exception {
final int totalMetadata = 4;
final String knownKeyPrefix = "knownKey";
final String knownValuePrefix = "knownValue";
final Target testTarget = testdataFactory.createTarget(targetId);
for (int index = 0; index < totalMetadata; index++) {
targetManagement.createMetaData(testTarget.getControllerId(), Lists.newArrayList(
entityFactory.generateTargetMetadata(knownKeyPrefix + index, knownValuePrefix + index)));
}
mockMvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/metadata",
testTarget.getControllerId())).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(APPLICATION_JSON_HAL_UTF))
.andDo(this.document.document(
pathParameters(
parameterWithName("targetId").description(ApiModelPropertiesGeneric.ITEM_ID)),
responseFields(fieldWithPath("total").description(ApiModelPropertiesGeneric.TOTAL_ELEMENTS),
fieldWithPath("size").type(JsonFieldType.NUMBER)
.description(ApiModelPropertiesGeneric.SIZE),
fieldWithPath("content").description(MgmtApiModelProperties.META_DATA),
fieldWithPath("content[].key").description(MgmtApiModelProperties.META_DATA_KEY),
fieldWithPath("content[].value").description(MgmtApiModelProperties.META_DATA_VALUE))));
}
@Test
@Description("Get a paged list of meta data for a target with defined page size and sorting by name descending and key starting with 'known'."
+ " Required Permission: " + SpPermission.READ_REPOSITORY)
public void getMetadataWithParameters() throws Exception {
final int totalMetadata = 4;
final String knownKeyPrefix = "knownKey";
final String knownValuePrefix = "knownValue";
final Target testTarget = testdataFactory.createTarget(targetId);
for (int index = 0; index < totalMetadata; index++) {
targetManagement.createMetaData(testTarget.getControllerId(), Lists.newArrayList(
entityFactory.generateTargetMetadata(knownKeyPrefix + index, knownValuePrefix + index)));
}
mockMvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/metadata",
testTarget.getControllerId()).param("offset", "1").param("limit", "2").param("sort", "key:DESC")
.param("q", "key==known*"))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(APPLICATION_JSON_HAL_UTF))
.andDo(this.document.document(
requestParameters(
parameterWithName("limit").attributes(key("type").value("query"))
.description(ApiModelPropertiesGeneric.LIMIT),
parameterWithName("sort").description(ApiModelPropertiesGeneric.SORT),
parameterWithName("offset").description(ApiModelPropertiesGeneric.OFFSET),
parameterWithName("q").description(ApiModelPropertiesGeneric.FIQL)),
responseFields(fieldWithPath("total").description(ApiModelPropertiesGeneric.TOTAL_ELEMENTS),
fieldWithPath("size").type(JsonFieldType.NUMBER)
.description(ApiModelPropertiesGeneric.SIZE),
fieldWithPath("content").description(MgmtApiModelProperties.META_DATA),
fieldWithPath("content[].key").description(MgmtApiModelProperties.META_DATA_KEY),
fieldWithPath("content[].value").description(MgmtApiModelProperties.META_DATA_VALUE))));
}
@Test
@Description("Get a single meta data value for a meta data key." + " Required Permission: "
+ SpPermission.READ_REPOSITORY)
public void getMetadataValue() throws Exception {
// prepare and create metadata
final String knownKey = "knownKey";
final String knownValue = "knownValue";
final Target testTarget = testdataFactory.createTarget(targetId);
targetManagement.createMetaData(testTarget.getControllerId(),
Arrays.asList(entityFactory.generateTargetMetadata(knownKey, knownValue)));
mockMvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/metadata/{metadatakey}",
testTarget.getControllerId(), knownKey)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andDo(this.document.document(
pathParameters(parameterWithName("targetId").description(ApiModelPropertiesGeneric.ITEM_ID),
parameterWithName("metadatakey").description(ApiModelPropertiesGeneric.ITEM_ID)),
responseFields(fieldWithPath("key").description(MgmtApiModelProperties.META_DATA_KEY),
fieldWithPath("value").description(MgmtApiModelProperties.META_DATA_VALUE))));
}
@Test
@Description("Update a single meta data value for specific key." + " Required Permission: "
+ SpPermission.UPDATE_REPOSITORY)
public void updateMetadata() throws Exception {
// prepare and create metadata for update
final String knownKey = "knownKey";
final String knownValue = "knownValue";
final String updateValue = "valueForUpdate";
final Target testTarget = testdataFactory.createTarget(targetId);
targetManagement.createMetaData(testTarget.getControllerId(),
Arrays.asList(entityFactory.generateTargetMetadata(knownKey, knownValue)));
final JSONObject jsonObject = new JSONObject().put("key", knownKey).put("value", updateValue);
mockMvc.perform(put(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/metadata/{metadatakey}",
testTarget.getControllerId(), knownKey)
.contentType(MediaType.APPLICATION_JSON_UTF8).content(jsonObject.toString()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andDo(this.document.document(
pathParameters(parameterWithName("targetId").description(ApiModelPropertiesGeneric.ITEM_ID),
parameterWithName("metadatakey").description(ApiModelPropertiesGeneric.ITEM_ID)),
requestFields(requestFieldWithPath("key").description(MgmtApiModelProperties.META_DATA_KEY),
requestFieldWithPath("value").description(MgmtApiModelProperties.META_DATA_VALUE)),
responseFields(fieldWithPath("key").description(MgmtApiModelProperties.META_DATA_KEY),
fieldWithPath("value").description(MgmtApiModelProperties.META_DATA_VALUE))));
}
@Test
@Description("Delete a single meta data." + " Required Permission: " + SpPermission.UPDATE_REPOSITORY)
public void deleteMetadata() throws Exception {
// prepare and create metadata for deletion
final String knownKey = "knownKey";
final String knownValue = "knownValue";
final Target testTarget = testdataFactory.createTarget(targetId);
targetManagement.createMetaData(testTarget.getControllerId(),
Arrays.asList(entityFactory.generateTargetMetadata(knownKey, knownValue)));
mockMvc.perform(delete(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/metadata/{key}",
testTarget.getControllerId(), knownKey)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andDo(this.document.document(
pathParameters(parameterWithName("targetId").description(ApiModelPropertiesGeneric.ITEM_ID),
parameterWithName("key").description(ApiModelPropertiesGeneric.ITEM_ID))));
}
@Test
@Description("Create a list of meta data entries" + " Required Permission: " + SpPermission.READ_REPOSITORY
+ " and " + SpPermission.UPDATE_TARGET)
public void createMetadata() throws Exception {
final Target testTarget = testdataFactory.createTarget(targetId);
final String knownKey1 = "knownKey1";
final String knownKey2 = "knownKey2";
final String knownValue1 = "knownValue1";
final String knownValue2 = "knownValue2";
final JSONArray jsonArray = new JSONArray();
jsonArray.put(new JSONObject().put("key", knownKey1).put("value", knownValue1));
jsonArray.put(new JSONObject().put("key", knownKey2).put("value", knownValue2));
mockMvc.perform(
post(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/metadata",
testTarget.getControllerId()).contentType(MediaType.APPLICATION_JSON_UTF8)
.content(jsonArray.toString()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isCreated())
.andExpect(content().contentType(APPLICATION_JSON_HAL_UTF))
.andDo(this.document.document(
pathParameters(
parameterWithName("targetId").description(ApiModelPropertiesGeneric.ITEM_ID)),
requestFields(requestFieldWithPath("[]key").description(MgmtApiModelProperties.META_DATA_KEY),
optionalRequestFieldWithPath("[]value")
.description(MgmtApiModelProperties.META_DATA_VALUE))));
}
private String createTargetJsonForPostRequest(final String controllerId, final String name, private String createTargetJsonForPostRequest(final String controllerId, final String name,
final String description) throws JsonProcessingException { final String description) throws JsonProcessingException {
final Map<String, Object> target = new HashMap<>(); final Map<String, Object> target = new HashMap<>();

View File

@@ -124,6 +124,11 @@ public class HawkbitSecurityProperties {
*/ */
private int maxMetaDataEntriesPerDistributionSet = 100; private int maxMetaDataEntriesPerDistributionSet = 100;
/**
* Maximum number of meta data entries per target
*/
private int maxMetaDataEntriesPerTarget = 100;
/** /**
* Maximum number of software modules per distribution set * Maximum number of software modules per distribution set
*/ */
@@ -230,6 +235,14 @@ public class HawkbitSecurityProperties {
this.maxMetaDataEntriesPerDistributionSet = maxMetaDataEntriesPerDistributionSet; this.maxMetaDataEntriesPerDistributionSet = maxMetaDataEntriesPerDistributionSet;
} }
public int getMaxMetaDataEntriesPerTarget() {
return maxMetaDataEntriesPerTarget;
}
public void setMaxMetaDataEntriesPerTarget(final int maxMetaDataEntriesPerTarget) {
this.maxMetaDataEntriesPerTarget = maxMetaDataEntriesPerTarget;
}
public int getMaxSoftwareModulesPerDistributionSet() { public int getMaxSoftwareModulesPerDistributionSet() {
return maxSoftwareModulesPerDistributionSet; return maxSoftwareModulesPerDistributionSet;
} }

View File

@@ -52,7 +52,7 @@ public class DsMetadataPopupLayout extends AbstractMetadataPopupLayout<Distribut
protected DistributionSetMetadata createMetadata(final DistributionSet entity, final String key, protected DistributionSetMetadata createMetadata(final DistributionSet entity, final String key,
final String value) { final String value) {
final DistributionSetMetadata dsMetaData = distributionSetManagement final DistributionSetMetadata dsMetaData = distributionSetManagement
.createMetaData(entity.getId(), Arrays.asList(entityFactory.generateMetadata(key, value))).get(0); .createMetaData(entity.getId(), Arrays.asList(entityFactory.generateDsMetadata(key, value))).get(0);
setSelectedEntity(dsMetaData.getDistributionSet()); setSelectedEntity(dsMetaData.getDistributionSet());
return dsMetaData; return dsMetaData;
} }
@@ -61,7 +61,7 @@ public class DsMetadataPopupLayout extends AbstractMetadataPopupLayout<Distribut
protected DistributionSetMetadata updateMetadata(final DistributionSet entity, final String key, protected DistributionSetMetadata updateMetadata(final DistributionSet entity, final String key,
final String value) { final String value) {
final DistributionSetMetadata dsMetaData = distributionSetManagement.updateMetaData(entity.getId(), final DistributionSetMetadata dsMetaData = distributionSetManagement.updateMetaData(entity.getId(),
entityFactory.generateMetadata(key, value)); entityFactory.generateDsMetadata(key, value));
setSelectedEntity(dsMetaData.getDistributionSet()); setSelectedEntity(dsMetaData.getDistributionSet());
return dsMetaData; return dsMetaData;
} }