Skip to content

Commit

Permalink
config globale, con maxsize globale
Browse files Browse the repository at this point in the history
  • Loading branch information
gnespolino committed Feb 14, 2024
1 parent d891502 commit 5ebba25
Show file tree
Hide file tree
Showing 11 changed files with 421 additions and 9 deletions.
7 changes: 6 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,12 @@ jacocoTestCoverageVerification {
'it.gov.innovazione.ndc.harvester.SecurityUtils',
'it.gov.innovazione.ndc.*Exception',
'it.gov.innovazione.ndc.harvester.HarvesterJob',
'it.gov.innovazione.ndc.model.harvester.HarvesterRun.Status'
'it.gov.innovazione.ndc.model.harvester.HarvesterRun.Status',
'it.gov.innovazione.ndc.harvester.service.ActualConfigService',
'it.gov.innovazione.ndc.harvester.service.ActualConfigService.Validator',
'it.gov.innovazione.ndc.harvester.service.CachingConfigService',
'it.gov.innovazione.ndc.harvester.service.ConfigService.NdcConfiguration',
'it.gov.innovazione.ndc.harvester.service.ConfigService'
]
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package it.gov.innovazione.ndc.controller;

import it.gov.innovazione.ndc.harvester.service.ActualConfigService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import java.security.Principal;
import java.util.Map;

import static org.springframework.http.HttpStatus.ACCEPTED;
import static org.springframework.http.HttpStatus.CREATED;

@RestController
@RequiredArgsConstructor
@RequestMapping("/config/ndc")
@Slf4j
public class ConfigurationController {

private final ActualConfigService configService;

@GetMapping
public Map<ActualConfigService.ConfigKey, ActualConfigService.ConfigEntry> getConfig() {
return configService.getNdcConfiguration().getValue();
}

@PostMapping
@ResponseStatus(CREATED)
public void setConfig(
@RequestBody Map<ActualConfigService.ConfigKey, Object> config,
Principal principal) {
configService.setNdConfig(config, principal.getName());
}

@PutMapping("/{configKey}")
@ResponseStatus(ACCEPTED)
public void updateRepository(
@PathVariable ActualConfigService.ConfigKey configKey,
@RequestParam String value,
Principal principal) {
configService.writeConfigKey(configKey, principal.getName(), value);
}

@DeleteMapping("/{configKey}")
@ResponseStatus(ACCEPTED)
public void deleteRepository(
@PathVariable ActualConfigService.ConfigKey configKey,
Principal principal) {
configService.removeConfigKey(configKey, principal.getName());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import it.gov.innovazione.ndc.harvester.SemanticAssetType;
import it.gov.innovazione.ndc.harvester.exception.SinglePathProcessingException;
import it.gov.innovazione.ndc.harvester.model.SemanticAssetPath;
import it.gov.innovazione.ndc.harvester.service.ConfigService;
import it.gov.innovazione.ndc.model.harvester.Repository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -17,11 +18,14 @@
import java.nio.file.Path;
import java.util.List;

import static it.gov.innovazione.ndc.harvester.service.ActualConfigService.ConfigKey.MAX_FILE_SIZE_BYTES;

@Slf4j
@RequiredArgsConstructor
public abstract class BaseSemanticAssetHarvester<P extends SemanticAssetPath> implements SemanticAssetHarvester {
private final SemanticAssetType type;
private final NdcEventPublisher eventPublisher;
private final ConfigService configService;

@Override
public SemanticAssetType getType() {
Expand Down Expand Up @@ -53,7 +57,10 @@ private void notifyIfSizeExceed(P path) {
HarvestExecutionContext context = HarvestExecutionContextUtils.getContext();
if (context != null) {
List<File> files = path.getAllFiles();
if (isBiggerThan(context.getRepository().getMaxFileSizeBytes(), files)) {
Long maxFileSizeBytes = configService.getParsedOrGetDefault(
MAX_FILE_SIZE_BYTES,
() -> context.getRepository().getMaxFileSizeBytes());
if (isBiggerThan(maxFileSizeBytes, files)) {
notify(context, path);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import it.gov.innovazione.ndc.harvester.SemanticAssetType;
import it.gov.innovazione.ndc.harvester.model.CvPath;
import it.gov.innovazione.ndc.harvester.pathprocessors.ControlledVocabularyPathProcessor;
import it.gov.innovazione.ndc.harvester.service.ConfigService;
import org.springframework.stereotype.Component;

import java.nio.file.Path;
Expand All @@ -15,8 +16,8 @@ public class ControlledVocabularyHarvester extends BaseSemanticAssetHarvester<Cv
private final AgencyRepositoryService agencyRepositoryService;
private final ControlledVocabularyPathProcessor pathProcessor;

public ControlledVocabularyHarvester(AgencyRepositoryService agencyRepositoryService, ControlledVocabularyPathProcessor pathProcessor, NdcEventPublisher ndcEventPublisher) {
super(SemanticAssetType.CONTROLLED_VOCABULARY, ndcEventPublisher);
public ControlledVocabularyHarvester(AgencyRepositoryService agencyRepositoryService, ControlledVocabularyPathProcessor pathProcessor, NdcEventPublisher ndcEventPublisher, ConfigService configService) {
super(SemanticAssetType.CONTROLLED_VOCABULARY, ndcEventPublisher, configService);
this.agencyRepositoryService = agencyRepositoryService;
this.pathProcessor = pathProcessor;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import it.gov.innovazione.ndc.harvester.SemanticAssetType;
import it.gov.innovazione.ndc.harvester.model.SemanticAssetPath;
import it.gov.innovazione.ndc.harvester.pathprocessors.OntologyPathProcessor;
import it.gov.innovazione.ndc.harvester.service.ConfigService;
import org.springframework.stereotype.Component;

import java.nio.file.Path;
Expand All @@ -15,8 +16,8 @@ public class OntologyHarvester extends BaseSemanticAssetHarvester<SemanticAssetP
private final AgencyRepositoryService agencyRepositoryService;
private final OntologyPathProcessor pathProcessor;

public OntologyHarvester(AgencyRepositoryService agencyRepositoryService, OntologyPathProcessor pathProcessor, NdcEventPublisher ndcEventPublisher) {
super(SemanticAssetType.ONTOLOGY, ndcEventPublisher);
public OntologyHarvester(AgencyRepositoryService agencyRepositoryService, OntologyPathProcessor pathProcessor, NdcEventPublisher ndcEventPublisher, ConfigService configService) {
super(SemanticAssetType.ONTOLOGY, ndcEventPublisher, configService);
this.agencyRepositoryService = agencyRepositoryService;
this.pathProcessor = pathProcessor;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import it.gov.innovazione.ndc.harvester.SemanticAssetType;
import it.gov.innovazione.ndc.harvester.model.SemanticAssetPath;
import it.gov.innovazione.ndc.harvester.pathprocessors.SchemaPathProcessor;
import it.gov.innovazione.ndc.harvester.service.ConfigService;
import org.springframework.stereotype.Component;

import java.nio.file.Path;
Expand All @@ -15,8 +16,8 @@ public class SchemaHarvester extends BaseSemanticAssetHarvester<SemanticAssetPat
private final AgencyRepositoryService agencyRepositoryService;
private final SchemaPathProcessor pathProcessor;

public SchemaHarvester(AgencyRepositoryService agencyRepositoryService, SchemaPathProcessor pathProcessor, NdcEventPublisher ndcEventPublisher) {
super(SemanticAssetType.SCHEMA, ndcEventPublisher);
public SchemaHarvester(AgencyRepositoryService agencyRepositoryService, SchemaPathProcessor pathProcessor, NdcEventPublisher ndcEventPublisher, ConfigService configService) {
super(SemanticAssetType.SCHEMA, ndcEventPublisher, configService);
this.agencyRepositoryService = agencyRepositoryService;
this.pathProcessor = pathProcessor;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
package it.gov.innovazione.ndc.harvester.service;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import it.gov.innovazione.ndc.eventhandler.NdcEventPublisher;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import java.time.Instant;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
@RequiredArgsConstructor
public class ActualConfigService implements ConfigService {

private static final String CONFIG_ID = "ndc";
private static final TypeReference<Map<ConfigKey, ConfigEntry>> TYPE_REF = new TypeReference<>() {
};
private final JdbcTemplate jdbcTemplate;
private final ObjectMapper objectMapper;
private final NdcEventPublisher ndcEventPublisher;

@Override
public NdcConfiguration getNdcConfiguration() {
try {
return jdbcTemplate.queryForObject(
"SELECT * FROM CONFIGURATION WHERE ID = ?",
new Object[]{CONFIG_ID},
(rs, rowNum) -> NdcConfiguration.of(readSafely(rs.getString("VALUE"))));
} catch (Exception e) {
return NdcConfiguration.of(Map.of());
}
}

@Override
public void writeConfigKey(ConfigKey key, String writtenBy, Object value) {
validateOne(key, value);
ConfigEntry oldValueIfExist = null;
try {
Map<ConfigKey, ConfigEntry> config = new HashMap<>(getNdcConfiguration().getValue());

if (config.containsKey(key)) {
oldValueIfExist = config.get(key);
}

ConfigEntry newValue = ConfigEntry.builder()
.writtenBy(writtenBy)
.writtenAt(Instant.now())
.value(value)
.build();

config.put(key, newValue);
writeConfig(config);
sendConfigWrittenEvent(
"write-config-key",
Map.of(key, ConfigChange.builder()
.oldValue(oldValueIfExist)
.newValue(newValue)
.build()), writtenBy);
} catch (Exception e) {
sendConfigWriteErrorEvent(
"write-config-key",
Map.of(key, ConfigChange.builder()
.oldValue(oldValueIfExist)
.build()), writtenBy, e);
}

}

private void validate(Map<ConfigKey, Object> config) {
config.forEach(this::validateOne);
}

private void validateOne(ConfigKey key, Object value) {
if (value == null) {
throw new IllegalArgumentException("Value cannot be null");
}
if (!key.getValidator().validator.test(String.valueOf(value))) {
throw new IllegalArgumentException("Value " + value + " is not valid for " + key + " key, using validator " + key.getValidator().name());
}
}

private void sendConfigWrittenEvent(String operation, Map<ConfigKey, ConfigChange> changes, String writtenBy) {
ndcEventPublisher.publishEvent("config", "config." + operation, null, writtenBy,
ConfigEvent.builder()
.changes(changes)
.build());
}

private void sendConfigWriteErrorEvent(String operation, Map<ConfigKey, ConfigChange> changes, String writtenBy, Exception exception) {
ndcEventPublisher.publishEvent("config", "config." + operation + ".error", null, writtenBy,
ConfigEvent.builder()
.changes(changes)
.error(exception)
.build());
}

@Override
public void setNdConfig(Map<ConfigKey, Object> config, String writtenBy) {
validate(config);
try {
Map<ConfigKey, ConfigEntry> oldConfig = getNdcConfiguration().getValue();
Map<ConfigKey, ConfigEntry> newConfig = config.entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
entry -> ConfigEntry.builder()
.writtenBy(writtenBy)
.writtenAt(Instant.now())
.value(entry.getValue())
.build()));
writeConfig(newConfig);

Map<ConfigKey, ConfigChange> changes = Stream.concat(oldConfig.keySet().stream(), newConfig.keySet().stream())
.distinct()
.collect(Collectors.toMap(Function.identity(),
key -> ConfigChange.builder()
.oldValue(oldConfig.get(key))
.newValue(newConfig.get(key))
.build()));

sendConfigWrittenEvent("write-config", changes, writtenBy);
} catch (Exception e) {
sendConfigWriteErrorEvent(
"write-config",
config.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, entry -> ConfigChange.builder()
.oldValue(null)
.build())),
writtenBy, e);
}
}

@SneakyThrows
private void writeConfig(Map<ConfigKey, ConfigEntry> config) {
String valueAsString = objectMapper.writeValueAsString(config);
jdbcTemplate.update(
"INSERT INTO CONFIGURATION (ID, VALUE) VALUES (?, ?) ON DUPLICATE KEY UPDATE VALUE = ?",
CONFIG_ID, valueAsString, valueAsString);
}

@SneakyThrows
private Map<ConfigKey, ConfigEntry> readSafely(String value) {
try {
return objectMapper.readValue(value, TYPE_REF);
} catch (Exception e) {
return Map.of();
}
}

@Override
public void removeConfigKey(ConfigKey configKey, String writtenBy) {
ConfigEntry oldValue = null;
try {
Map<ConfigKey, ConfigEntry> config = new EnumMap<>(getNdcConfiguration().getValue());
if (config.containsKey(configKey)) {
oldValue = config.get(configKey);
}
config.remove(configKey);
writeConfig(config);
sendConfigWrittenEvent(
"remove-config-key",
Map.of(configKey, ConfigChange.builder()
.oldValue(oldValue)
.build()), writtenBy);
} catch (Exception e) {
sendConfigWriteErrorEvent(
"remove-config-key",
Map.of(configKey, ConfigChange.builder()
.oldValue(oldValue)
.build()), writtenBy, e);
}
}

@Getter
@RequiredArgsConstructor
public enum ConfigKey {
MAX_FILE_SIZE_BYTES("The maximum file size in bytes of a file to be harvested", Validator.IS_LONG, Parser.TO_LONG),;

private final String description;
private final Validator validator;
private final Parser parser;
}

@Getter
@RequiredArgsConstructor
private enum Validator {
IS_LONG(s -> {
try {
Long.parseLong(s);
return true;
} catch (Exception e) {
return false;
}
});
private final Predicate<String> validator;
}

@Getter
@RequiredArgsConstructor
public enum Parser {
TO_LONG(Long::parseLong);
private final Function<String, Object> parser;
}
}
Loading

0 comments on commit 5ebba25

Please sign in to comment.