com.github.nagyesta.lowkeyvault.service.common.impl.BaseVaultFakeImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of lowkey-vault-app Show documentation
Show all versions of lowkey-vault-app Show documentation
Assembled application of Lowkey Vault.
package com.github.nagyesta.lowkeyvault.service.common.impl;
import com.github.nagyesta.lowkeyvault.model.v7_2.common.constants.RecoveryLevel;
import com.github.nagyesta.lowkeyvault.service.EntityId;
import com.github.nagyesta.lowkeyvault.service.common.BaseVaultEntity;
import com.github.nagyesta.lowkeyvault.service.common.BaseVaultFake;
import com.github.nagyesta.lowkeyvault.service.common.ReadOnlyVersionedEntityMultiMap;
import com.github.nagyesta.lowkeyvault.service.common.VersionedEntityMultiMap;
import com.github.nagyesta.lowkeyvault.service.exception.AlreadyExistsException;
import com.github.nagyesta.lowkeyvault.service.exception.NotFoundException;
import com.github.nagyesta.lowkeyvault.service.vault.VaultFake;
import lombok.NonNull;
import org.springframework.util.Assert;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;
/**
* The base interface of the vault fakes.
*
* @param The type of the key (not versioned).
* @param The versioned key type.
* @param The read-only entity type.
* @param The modifiable entity type.
*/
public abstract class BaseVaultFakeImpl, ME extends RE>
implements BaseVaultFake {
private final VaultFake vaultFake;
private final VersionedEntityMultiMap entities;
private final VersionedEntityMultiMap deletedEntities;
protected BaseVaultFakeImpl(@NonNull final VaultFake vaultFake,
@NonNull final RecoveryLevel recoveryLevel,
final Integer recoverableDays) {
recoveryLevel.checkValidRecoverableDays(recoverableDays);
this.vaultFake = vaultFake;
entities = new ConcurrentVersionedEntityMultiMap<>(
recoveryLevel, recoverableDays, this::createVersionedId, false);
deletedEntities = new ConcurrentVersionedEntityMultiMap<>(
recoveryLevel, recoverableDays, this::createVersionedId, true);
}
@Override
public ReadOnlyVersionedEntityMultiMap getEntities() {
return entities;
}
@Override
public ReadOnlyVersionedEntityMultiMap getDeletedEntities() {
return deletedEntities;
}
@Override
public void clearTags(@NonNull final V entityId) {
entities.getEntity(entityId).setTags(new TreeMap<>());
}
@Override
public void addTags(@NonNull final V entityId, final Map tags) {
final TreeMap newTags = new TreeMap<>(entities.getEntity(entityId).getTags());
newTags.putAll(Objects.requireNonNullElse(tags, Collections.emptyMap()));
entities.getEntity(entityId).setTags(newTags);
}
@Override
public void setEnabled(@NonNull final V entityId, final boolean enabled) {
entities.getEntity(entityId).setEnabled(enabled);
}
@Override
public void setExpiry(@NonNull final V entityId,
final OffsetDateTime notBefore,
final OffsetDateTime expiry) {
if (expiry != null && notBefore != null && notBefore.isAfter(expiry)) {
throw new IllegalArgumentException("Expiry cannot be before notBefore.");
}
final ME entity = entities.getEntity(entityId);
entity.setNotBefore(notBefore);
entity.setExpiry(expiry);
}
protected void setCreatedAndUpdatedOn(final V entityId, final OffsetDateTime created, final OffsetDateTime updated) {
if (created == null && updated == null) {
return;
}
final OffsetDateTime createdOn = Optional.ofNullable(created).orElse(OffsetDateTime.now(ZoneOffset.UTC));
final OffsetDateTime updatedOn = Optional.ofNullable(updated).orElse(createdOn);
if (createdOn.isAfter(updatedOn)) {
throw new IllegalArgumentException("Updated cannot be before created.");
}
final ME entity = entities.getEntity(entityId);
entity.setCreatedOn(createdOn);
entity.setUpdatedOn(updatedOn);
}
@Override
public void delete(@NonNull final K entityId) {
if (!entities.containsName(entityId.id())) {
throw new NotFoundException("Entity not found: " + entityId);
}
entities.moveTo(entityId, deletedEntities, this::markDeleted);
}
@Override
public void recover(@NonNull final K entityId) {
deletedEntities.purgeExpired();
if (!deletedEntities.containsName(entityId.id())) {
throw new NotFoundException("Entity not found: " + entityId);
}
deletedEntities.moveTo(entityId, entities, this::markRestored);
}
@Override
public void purge(@NonNull final K entityId) {
deletedEntities.purgeExpired();
if (!deletedEntities.containsName(entityId.id())) {
throw new NotFoundException("Entity not found: " + entityId);
}
deletedEntities.purgeDeleted(entityId);
}
@Override
public void timeShift(final int offsetSeconds) {
Assert.isTrue(offsetSeconds > 0, "Offset must be positive.");
this.entities.forEachEntity(entity -> entity.timeShift(offsetSeconds));
this.deletedEntities.forEachEntity(entity -> entity.timeShift(offsetSeconds));
this.deletedEntities.purgeExpired();
}
protected Set keepNamesReadyForRemoval(final Set names) {
return names.stream()
.filter(n -> !this.getEntities().containsName(n))
.filter(n -> !this.getDeletedEntities().containsName(n))
.collect(Collectors.toSet());
}
protected void setManaged(final V entityId, final boolean managed) {
entities.getEntity(entityId).setManaged(managed);
}
protected abstract V createVersionedId(String id, String version);
protected VersionedEntityMultiMap getEntitiesInternal() {
return entities;
}
protected VersionedEntityMultiMap getDeletedEntitiesInternal() {
return deletedEntities;
}
protected VaultFake vaultFake() {
return vaultFake;
}
protected V addVersion(@org.springframework.lang.NonNull final V entityId,
@org.springframework.lang.NonNull final ME entity) {
assertNoConflict(entityId);
entities.put(entityId, entity);
return entityId;
}
private void assertNoConflict(final V entityId) {
deletedEntities.purgeExpired();
if (!entities.containsName(entityId.id()) && deletedEntities.containsName(entityId.id())) {
throw new AlreadyExistsException("A deleted entity already exists with this name: " + entityId);
}
}
private ME markDeleted(final ME entity) {
final int days = entity.getRecoverableDays();
final OffsetDateTime now = OffsetDateTime.now(ZoneOffset.UTC).truncatedTo(ChronoUnit.SECONDS);
entity.setDeletedDate(now);
entity.setScheduledPurgeDate(now.plusDays(days));
return entity;
}
private ME markRestored(final ME entity) {
entity.setDeletedDate(null);
entity.setScheduledPurgeDate(null);
return entity;
}
}