diff --git a/src/main/java/fr/inra/oresing/checker/CheckerOnOneVariableComponentLineChecker.java b/src/main/java/fr/inra/oresing/checker/CheckerOnOneVariableComponentLineChecker.java index d85a994f8866a577cb77f9b39e14ffac09450062..4c3a9420dd19a2557503c4c1f80f7c48370a136f 100644 --- a/src/main/java/fr/inra/oresing/checker/CheckerOnOneVariableComponentLineChecker.java +++ b/src/main/java/fr/inra/oresing/checker/CheckerOnOneVariableComponentLineChecker.java @@ -6,8 +6,8 @@ import fr.inra.oresing.model.ReferenceColumn; import fr.inra.oresing.model.ReferenceDatum; import fr.inra.oresing.model.VariableComponentKey; import fr.inra.oresing.persistence.SqlPrimitiveType; -import fr.inra.oresing.rest.DefaultValidationCheckResult; import fr.inra.oresing.rest.ValidationCheckResult; +import fr.inra.oresing.rest.validationcheckresults.DefaultValidationCheckResult; import fr.inra.oresing.transformer.LineTransformer; import org.assertj.core.util.Strings; diff --git a/src/main/java/fr/inra/oresing/checker/DateLineChecker.java b/src/main/java/fr/inra/oresing/checker/DateLineChecker.java index cd6d06289ca0e1e3602abf3e69b277dfb9ee2902..b1ba76f25661e6f063252ca02f68063ead7c9ae4 100644 --- a/src/main/java/fr/inra/oresing/checker/DateLineChecker.java +++ b/src/main/java/fr/inra/oresing/checker/DateLineChecker.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.google.common.collect.ImmutableMap; import fr.inra.oresing.persistence.SqlPrimitiveType; import fr.inra.oresing.rest.ValidationCheckResult; +import fr.inra.oresing.rest.validationcheckresults.DateValidationCheckResult; import fr.inra.oresing.transformer.LineTransformer; import java.time.format.DateTimeFormatter; diff --git a/src/main/java/fr/inra/oresing/checker/FloatChecker.java b/src/main/java/fr/inra/oresing/checker/FloatChecker.java index 5e86a8adef9655e6c9b8654192d5f4fd44ae3c45..7038e6b8b89949f0f40b126cace2589b94ab01d5 100644 --- a/src/main/java/fr/inra/oresing/checker/FloatChecker.java +++ b/src/main/java/fr/inra/oresing/checker/FloatChecker.java @@ -2,8 +2,8 @@ package fr.inra.oresing.checker; import com.fasterxml.jackson.annotation.JsonIgnore; import com.google.common.collect.ImmutableMap; +import fr.inra.oresing.rest.validationcheckresults.DefaultValidationCheckResult; import fr.inra.oresing.persistence.SqlPrimitiveType; -import fr.inra.oresing.rest.DefaultValidationCheckResult; import fr.inra.oresing.rest.ValidationCheckResult; import fr.inra.oresing.transformer.LineTransformer; @@ -53,4 +53,4 @@ public class FloatChecker implements CheckerOnOneVariableComponentLineChecker<Fl public SqlPrimitiveType getSqlType() { return SqlPrimitiveType.NUMERIC; } -} \ No newline at end of file +} diff --git a/src/main/java/fr/inra/oresing/checker/GroovyLineChecker.java b/src/main/java/fr/inra/oresing/checker/GroovyLineChecker.java index fb29c552e884e62e8096407007e6bc74b20290e6..fa2444058c1aec46020c4faa78498f166f411b81 100644 --- a/src/main/java/fr/inra/oresing/checker/GroovyLineChecker.java +++ b/src/main/java/fr/inra/oresing/checker/GroovyLineChecker.java @@ -6,7 +6,7 @@ import fr.inra.oresing.groovy.GroovyExpression; import fr.inra.oresing.model.Datum; import fr.inra.oresing.model.ReferenceDatum; import fr.inra.oresing.model.SomethingThatCanProvideEvaluationContext; -import fr.inra.oresing.rest.DefaultValidationCheckResult; +import fr.inra.oresing.rest.validationcheckresults.DefaultValidationCheckResult; import fr.inra.oresing.rest.ValidationCheckResult; import java.util.Optional; diff --git a/src/main/java/fr/inra/oresing/checker/IntegerChecker.java b/src/main/java/fr/inra/oresing/checker/IntegerChecker.java index b442755d1c10959b4923f87c668435c922e95236..d5693c1efe45573b0daac0ed9a4aa019b1eac3b2 100644 --- a/src/main/java/fr/inra/oresing/checker/IntegerChecker.java +++ b/src/main/java/fr/inra/oresing/checker/IntegerChecker.java @@ -3,8 +3,8 @@ package fr.inra.oresing.checker; import com.fasterxml.jackson.annotation.JsonIgnore; import com.google.common.collect.ImmutableMap; import fr.inra.oresing.persistence.SqlPrimitiveType; -import fr.inra.oresing.rest.DefaultValidationCheckResult; import fr.inra.oresing.rest.ValidationCheckResult; +import fr.inra.oresing.rest.validationcheckresults.DefaultValidationCheckResult; import fr.inra.oresing.transformer.LineTransformer; public class IntegerChecker implements CheckerOnOneVariableComponentLineChecker<IntegerCheckerConfiguration> { diff --git a/src/main/java/fr/inra/oresing/checker/InvalidDatasetContentException.java b/src/main/java/fr/inra/oresing/checker/InvalidDatasetContentException.java index c783e9079ca3189260759aa17f24d8e7d6c8caea..a730d38767af31754b51ee90163803f893af4332 100644 --- a/src/main/java/fr/inra/oresing/checker/InvalidDatasetContentException.java +++ b/src/main/java/fr/inra/oresing/checker/InvalidDatasetContentException.java @@ -3,7 +3,7 @@ package fr.inra.oresing.checker; import com.google.common.collect.*; import fr.inra.oresing.OreSiTechnicalException; import fr.inra.oresing.rest.CsvRowValidationCheckResult; -import fr.inra.oresing.rest.DefaultValidationCheckResult; +import fr.inra.oresing.rest.validationcheckresults.DefaultValidationCheckResult; import fr.inra.oresing.rest.ValidationCheckResult; import java.util.List; diff --git a/src/main/java/fr/inra/oresing/checker/ReferenceLineChecker.java b/src/main/java/fr/inra/oresing/checker/ReferenceLineChecker.java index 48573745ade35044a4d92c062ebe2989c03a1e1b..421221db31b01fbbb6254715cd04dcff8990da82 100644 --- a/src/main/java/fr/inra/oresing/checker/ReferenceLineChecker.java +++ b/src/main/java/fr/inra/oresing/checker/ReferenceLineChecker.java @@ -3,6 +3,7 @@ package fr.inra.oresing.checker; import com.fasterxml.jackson.annotation.JsonIgnore; import com.google.common.collect.ImmutableMap; import fr.inra.oresing.persistence.SqlPrimitiveType; +import fr.inra.oresing.rest.validationcheckresults.ReferenceValidationCheckResult; import fr.inra.oresing.transformer.LineTransformer; import java.util.UUID; @@ -70,4 +71,4 @@ public class ReferenceLineChecker implements CheckerOnOneVariableComponentLineCh public SqlPrimitiveType getSqlType() { return SqlPrimitiveType.TEXT; } -} \ No newline at end of file +} diff --git a/src/main/java/fr/inra/oresing/checker/RegularExpressionChecker.java b/src/main/java/fr/inra/oresing/checker/RegularExpressionChecker.java index 2efb0ab79c29b6dd7a455ec59fef5d1c5788744f..e0cd07418f62ee69bfc9b2ed5c29a374a2221b65 100644 --- a/src/main/java/fr/inra/oresing/checker/RegularExpressionChecker.java +++ b/src/main/java/fr/inra/oresing/checker/RegularExpressionChecker.java @@ -2,8 +2,8 @@ package fr.inra.oresing.checker; import com.fasterxml.jackson.annotation.JsonIgnore; import com.google.common.collect.ImmutableMap; +import fr.inra.oresing.rest.validationcheckresults.DefaultValidationCheckResult; import fr.inra.oresing.persistence.SqlPrimitiveType; -import fr.inra.oresing.rest.DefaultValidationCheckResult; import fr.inra.oresing.rest.ValidationCheckResult; import fr.inra.oresing.transformer.LineTransformer; @@ -61,4 +61,4 @@ public class RegularExpressionChecker implements CheckerOnOneVariableComponentLi public SqlPrimitiveType getSqlType() { return SqlPrimitiveType.TEXT; } -} \ No newline at end of file +} diff --git a/src/main/java/fr/inra/oresing/model/ReferenceDatum.java b/src/main/java/fr/inra/oresing/model/ReferenceDatum.java index 8ac944ecee78acc8b0259fbd03b54576f970796e..8c912fa63a185b3a64847ca0791fd4dc7413aefc 100644 --- a/src/main/java/fr/inra/oresing/model/ReferenceDatum.java +++ b/src/main/java/fr/inra/oresing/model/ReferenceDatum.java @@ -6,7 +6,6 @@ import java.util.LinkedHashMap; import java.util.Map; public class ReferenceDatum implements SomethingThatCanProvideEvaluationContext { - private final Map<ReferenceColumn, String> values; public ReferenceDatum() { @@ -46,4 +45,4 @@ public class ReferenceDatum implements SomethingThatCanProvideEvaluationContext public ImmutableMap<String, Object> getEvaluationContext() { return ImmutableMap.of("datum", asMap()); } -} +} \ No newline at end of file diff --git a/src/main/java/fr/inra/oresing/rest/ConfigurationParsingResult.java b/src/main/java/fr/inra/oresing/rest/ConfigurationParsingResult.java index 6c6cea221dc1e8b94ad06288c627b09c080f012d..8d5cf59e91d01476ed9a5c8370ef12b2b3a6cd01 100644 --- a/src/main/java/fr/inra/oresing/rest/ConfigurationParsingResult.java +++ b/src/main/java/fr/inra/oresing/rest/ConfigurationParsingResult.java @@ -5,6 +5,7 @@ import com.google.common.collect.ImmutableSet; import fr.inra.oresing.groovy.GroovyExpression; import fr.inra.oresing.model.Configuration; import fr.inra.oresing.model.VariableComponentKey; +import fr.inra.oresing.rest.validationcheckresults.DefaultValidationCheckResult; import lombok.Value; import javax.annotation.Nullable; diff --git a/src/main/java/fr/inra/oresing/rest/OreSiService.java b/src/main/java/fr/inra/oresing/rest/OreSiService.java index d2d00b2d2b5c211cfdf74ccc1eaa59a9e3e50ff2..a1dec03be21af32daca88252cfce4ce2ef6df025 100644 --- a/src/main/java/fr/inra/oresing/rest/OreSiService.java +++ b/src/main/java/fr/inra/oresing/rest/OreSiService.java @@ -3,66 +3,23 @@ package fr.inra.oresing.rest; import com.google.common.base.Charsets; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableMultiset; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableSetMultimap; -import com.google.common.collect.ImmutableSortedSet; -import com.google.common.collect.Iterables; -import com.google.common.collect.Iterators; -import com.google.common.collect.Maps; -import com.google.common.collect.MoreCollectors; -import com.google.common.collect.Ordering; -import com.google.common.collect.SetMultimap; -import com.google.common.collect.Sets; +import com.google.common.collect.*; import com.google.common.primitives.Ints; import fr.inra.oresing.OreSiTechnicalException; -import fr.inra.oresing.checker.CheckerFactory; -import fr.inra.oresing.checker.DateLineChecker; -import fr.inra.oresing.checker.DateValidationCheckResult; -import fr.inra.oresing.checker.FloatChecker; -import fr.inra.oresing.checker.IntegerChecker; -import fr.inra.oresing.checker.InvalidDatasetContentException; -import fr.inra.oresing.checker.LineChecker; -import fr.inra.oresing.checker.ReferenceLineChecker; -import fr.inra.oresing.checker.ReferenceLineCheckerConfiguration; -import fr.inra.oresing.checker.ReferenceValidationCheckResult; +import fr.inra.oresing.ValidationLevel; +import fr.inra.oresing.checker.*; import fr.inra.oresing.groovy.CommonExpression; import fr.inra.oresing.groovy.Expression; import fr.inra.oresing.groovy.GroovyContextHelper; import fr.inra.oresing.groovy.StringGroovyExpression; -import fr.inra.oresing.model.Application; -import fr.inra.oresing.model.Authorization; -import fr.inra.oresing.model.BinaryFile; -import fr.inra.oresing.model.BinaryFileDataset; -import fr.inra.oresing.model.Configuration; -import fr.inra.oresing.model.Data; -import fr.inra.oresing.model.Datum; -import fr.inra.oresing.model.LocalDateTimeRange; -import fr.inra.oresing.model.ReferenceColumn; -import fr.inra.oresing.model.ReferenceDatum; -import fr.inra.oresing.model.ReferenceValue; -import fr.inra.oresing.model.VariableComponentKey; +import fr.inra.oresing.model.*; import fr.inra.oresing.model.internationalization.Internationalization; import fr.inra.oresing.model.internationalization.InternationalizationDisplay; import fr.inra.oresing.model.internationalization.InternationalizationReferenceMap; -import fr.inra.oresing.persistence.AuthenticationService; -import fr.inra.oresing.persistence.BinaryFileInfos; -import fr.inra.oresing.persistence.DataRepository; -import fr.inra.oresing.persistence.DataRow; -import fr.inra.oresing.persistence.Ltree; -import fr.inra.oresing.persistence.OreSiRepository; -import fr.inra.oresing.persistence.ReferenceValueRepository; -import fr.inra.oresing.persistence.SqlPolicy; -import fr.inra.oresing.persistence.SqlSchema; -import fr.inra.oresing.persistence.SqlSchemaForApplication; -import fr.inra.oresing.persistence.SqlService; +import fr.inra.oresing.persistence.*; import fr.inra.oresing.persistence.roles.OreSiRightOnApplicationRole; import fr.inra.oresing.persistence.roles.OreSiUserRole; +import fr.inra.oresing.rest.validationcheckresults.*; import fr.inra.oresing.transformer.TransformerFactory; import lombok.Value; import lombok.extern.slf4j.Slf4j; @@ -94,20 +51,7 @@ import java.time.LocalDateTime; import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.UUID; +import java.util.*; import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; @@ -370,11 +314,13 @@ public class OreSiService { } public UUID addReference(Application app, String refType, MultipartFile file) throws IOException { + ReferenceValueRepository referenceValueRepository = repo.getRepository(app).referenceValue(); authenticationService.setRoleForClient(); UUID fileId = storeFile(app, file,""); Configuration conf = app.getConfiguration(); Configuration.ReferenceDescription ref = conf.getReferences().get(refType); + final ImmutableMap<String, UUID> storedReferences = referenceValueRepository.getReferenceIdPerKeys(refType); ImmutableSet<LineChecker> lineCheckers = checkerFactory.getReferenceValidationLineCheckers(app, refType); Optional<ReferenceLineChecker> selfLineChecker = lineCheckers.stream() @@ -390,6 +336,7 @@ public class OreSiService { Function<Ltree, Ltree> getHierarchicalReferenceFn; Map<Ltree, Ltree> buildedHierarchicalKeys = new HashMap<>(); Map<Ltree, Ltree> parentreferenceMap = new HashMap<>(); + ListMultimap<Ltree, Integer> hierarchicalKeys = LinkedListMultimap.create(); if (toUpdateCompositeReference.isPresent()) { Configuration.CompositeReferenceDescription compositeReferenceDescription = toUpdateCompositeReference.get(); boolean root = Iterables.get(compositeReferenceDescription.getComponents(), 0).getReference().equals(refType); @@ -413,7 +360,6 @@ public class OreSiService { getHierarchicalReferenceFn = (reference) -> reference; } - ReferenceValueRepository referenceValueRepository = repo.getRepository(app).referenceValue(); CSVFormat csvFormat = CSVFormat.DEFAULT .withDelimiter(ref.getSeparator()) @@ -423,7 +369,7 @@ public class OreSiService { Iterator<CSVRecord> linesIterator = csvParser.iterator(); CSVRecord headerRow = linesIterator.next(); ImmutableList<String> columns = Streams.stream(headerRow).collect(ImmutableList.toImmutableList()); - Function<CSVRecord, ReferenceDatum> csvRecordToLineAsMapFn = line -> { + Function<CSVRecord, RowWithReferenceDatum> csvRecordToLineAsMapFn = line -> { Iterator<String> currentHeader = columns.iterator(); ReferenceDatum referenceDatum = new ReferenceDatum(); line.forEach(value -> { @@ -431,15 +377,16 @@ public class OreSiService { ReferenceColumn referenceColumn = new ReferenceColumn(header); referenceDatum.put(referenceColumn, value); }); - return referenceDatum; + int lineNumber = Ints.checkedCast(line.getRecordNumber()); + return new RowWithReferenceDatum(lineNumber, referenceDatum); }; List<CsvRowValidationCheckResult> rowErrors = new LinkedList<>(); Stream<CSVRecord> recordStream = Streams.stream(csvParser); if (isRecursive) { - recordStream = addMissingReferences(recordStream, selfLineChecker, recursiveComponentDescription, columns, ref, parentreferenceMap); + recordStream = addMissingReferences(recordStream, selfLineChecker, recursiveComponentDescription, columns, ref, parentreferenceMap, rowErrors, refType); + InvalidDatasetContentException.checkErrorsIsEmpty(rowErrors); } - List<Ltree> hierarchicalKeys = new LinkedList<>(); Optional<InternationalizationReferenceMap> internationalizationReferenceMap = Optional.ofNullable(conf) .map(configuration -> conf.getInternationalization()) .map(inter -> inter.getReferences()) @@ -452,7 +399,8 @@ public class OreSiService { .map(internationalizationDisplay -> internationalizationDisplay.getPattern()); Stream<ReferenceValue> referenceValuesStream = recordStream .map(csvRecordToLineAsMapFn) - .map(referenceDatum -> { + .map(rowWithReferenceDatum -> { + ReferenceDatum referenceDatum = rowWithReferenceDatum.getReferenceDatum(); Map<String, Set<UUID>> refsLinkedTo = new LinkedHashMap<>(); lineCheckers.forEach(lineChecker -> { ValidationCheckResult validationCheckResult = lineChecker.checkReference(referenceDatum); @@ -467,18 +415,24 @@ public class OreSiService { .add(referenceId); } } else { - rowErrors.add(new CsvRowValidationCheckResult(validationCheckResult, csvParser.getCurrentLineNumber())); + rowErrors.add(new CsvRowValidationCheckResult(validationCheckResult, rowWithReferenceDatum.getLineNumber())); } }); - ReferenceValue e = new ReferenceValue(); + final ReferenceValue e = new ReferenceValue(); Preconditions.checkState(!ref.getColumns().isEmpty(), "aucune colonne désignée comme clé naturelle pour le référentiel " + refType); - String naturalKeyAsString = ref.getKeyColumns().stream() - .map(ReferenceColumn::new) - .map(referenceDatum::get) - .filter(StringUtils::isNotEmpty) - .map(Ltree::escapeToLabel) - .collect(Collectors.joining(KEYCOLUMN_SEPARATOR)); - Ltree naturalKey = Ltree.fromSql(naturalKeyAsString); + Ltree naturalKey; + if (ref.getKeyColumns().isEmpty()) { + UUID technicalId = e.getId(); + naturalKey = Ltree.fromUuid(technicalId); + } else { + String naturalKeyAsString = ref.getKeyColumns().stream() + .map(ReferenceColumn::new) + .map(referenceDatum::get) + .filter(StringUtils::isNotEmpty) + .map(Ltree::escapeToLabel) + .collect(Collectors.joining(KEYCOLUMN_SEPARATOR)); + naturalKey = Ltree.fromSql(naturalKeyAsString); + } Ltree recursiveNaturalKey = naturalKey; final Ltree refTypeAsLabel = Ltree.fromUnescapedString(refType); if (isRecursive) { @@ -486,7 +440,9 @@ public class OreSiService { .map(referenceLineChecker -> referenceLineChecker.getReferenceValues()) .map(values -> values.get(naturalKey.getSql())) .filter(key -> key != null) - .ifPresent(key -> e.setId(key)); + .ifPresent(key -> { + e.setId(key); + }); Ltree parentKey = parentreferenceMap.getOrDefault(recursiveNaturalKey, null); while (parentKey != null) { recursiveNaturalKey = Ltree.join(parentKey, recursiveNaturalKey); @@ -508,6 +464,15 @@ public class OreSiService { getHierarchicalReferenceFn.apply(selfHierarchicalReference); referenceDatum.putAll(InternationalizationDisplay.getDisplays(displayPattern, displayColumns, referenceDatum)); buildedHierarchicalKeys.put(naturalKey, hierarchicalKey); + + /** + * on remplace l'id par celle en base si elle existe + * a noter que pour les references récursives on récupère l'id depuis referenceLineChecker.getReferenceValues() ce qui revient au même + */ + + if (storedReferences.containsKey(hierarchicalKey.getSql())) { + e.setId(storedReferences.get(hierarchicalKey.getSql())); + } e.setBinaryFile(fileId); e.setReferenceType(refType); e.setHierarchicalKey(hierarchicalKey); @@ -516,22 +481,18 @@ public class OreSiService { e.setNaturalKey(naturalKey); e.setApplication(app.getId()); e.setRefValues(referenceDatum.asMap()); - return e; - }) - .sorted(Comparator.comparing(a -> a.getHierarchicalKey().getSql())) - .map(e -> { - if (hierarchicalKeys.contains(e.getHierarchicalKey())) { - /*envoyer un message de warning : le refType avec la clef e.getNaturalKey existe en plusieurs exemplaires - dans le fichier. Seule la première ligne est enregistrée - */ -// ValidationCheckResult validationCheckResult = new ValidationCheckResult() -// rowErrors.add(new CsvRowValidationCheckResult(validationCheckResult, csvParser.getCurrentLineNumber())); + if (hierarchicalKeys.containsKey(e.getHierarchicalKey())) { + ValidationCheckResult validationCheckResult = new DuplicationLineValidationCheckResult(DuplicationLineValidationCheckResult.FileType.REFERENCES, refType, ValidationLevel.ERROR, e.getHierarchicalKey(), rowWithReferenceDatum.getLineNumber(), hierarchicalKeys.get(e.getHierarchicalKey())); + rowErrors.add(new CsvRowValidationCheckResult(validationCheckResult, rowWithReferenceDatum.getLineNumber())); + hierarchicalKeys.put(e.getHierarchicalKey(), rowWithReferenceDatum.getLineNumber()); + return null; } else { - hierarchicalKeys.add(e.getHierarchicalKey()); + hierarchicalKeys.put(e.getHierarchicalKey(), rowWithReferenceDatum.getLineNumber()); + return e; } - return e; }) - .filter(e -> e != null); + .filter(e -> e != null) + .sorted(Comparator.comparing(a -> a.getHierarchicalKey().getSql())); referenceValueRepository.storeAll(referenceValuesStream); InvalidDatasetContentException.checkErrorsIsEmpty(rowErrors); } @@ -539,11 +500,12 @@ public class OreSiService { return fileId; } - private Stream<CSVRecord> addMissingReferences(Stream<CSVRecord> recordStream, Optional<ReferenceLineChecker> selfLineChecker, Optional<Configuration.CompositeReferenceComponentDescription> recursiveComponentDescription, ImmutableList<String> columns, Configuration.ReferenceDescription ref, Map<Ltree, Ltree> referenceMap) { + private Stream<CSVRecord> addMissingReferences(Stream<CSVRecord> recordStream, Optional<ReferenceLineChecker> selfLineChecker, Optional<Configuration.CompositeReferenceComponentDescription> recursiveComponentDescription, ImmutableList<String> columns, Configuration.ReferenceDescription ref, Map<Ltree, Ltree> referenceMap, List<CsvRowValidationCheckResult> rowErrors, String refType) { Integer parentRecursiveIndex = recursiveComponentDescription .map(rcd -> rcd.getParentRecursiveKey()) .map(rck -> columns.indexOf(rck)) .orElse(null); + ListMultimap<String, Long> missingParentReferences = LinkedListMultimap.create(); if (parentRecursiveIndex == null || parentRecursiveIndex < 0) { return recordStream; } @@ -554,31 +516,44 @@ public class OreSiService { List<CSVRecord> collect = recordStream .peek(csvrecord -> { String s = csvrecord.get(parentRecursiveIndex); + String naturalKey = ref.getKeyColumns() + .stream() + .map(kc -> columns.indexOf(kc)) + .map(k -> Strings.isNullOrEmpty(csvrecord.get(k))?null:Ltree.escapeToLabel(csvrecord.get(k))) + .filter(k->k!=null) + .collect(Collectors.joining("__")); + if (!referenceUUIDs.containsKey(naturalKey)) { + referenceUUIDs.put(naturalKey, UUID.randomUUID()); + } if (!Strings.isNullOrEmpty(s)) { - String naturalKey; try { s = Ltree.escapeToLabel(s); - naturalKey = ref.getKeyColumns() - .stream() - .map(kc -> columns.indexOf(kc)) - .map(k -> Ltree.escapeToLabel(csvrecord.get(k))) - .collect(Collectors.joining("__")); } catch (IllegalArgumentException e) { return; } referenceMap.put(Ltree.fromSql(naturalKey), Ltree.fromUnescapedString(s)); if (!referenceUUIDs.containsKey(s)) { - referenceUUIDs.put(s, UUID.randomUUID()); - } - if (!referenceUUIDs.containsKey(naturalKey)) { - referenceUUIDs.put(naturalKey, UUID.randomUUID()); + final UUID uuid = UUID.randomUUID(); + referenceUUIDs.put(s, uuid); + missingParentReferences.put(s, csvrecord.getRecordNumber()); } } + missingParentReferences.removeAll(naturalKey); return; }) .collect(Collectors.toList()); selfLineChecker .ifPresent(slc -> slc.setReferenceValues(ImmutableMap.copyOf(referenceUUIDs))); + + if (!missingParentReferences.isEmpty()) { + missingParentReferences.asMap().entrySet().stream() + .forEach(entry -> { + final String missingParentReference = entry.getKey(); + entry.getValue().stream().forEach( + lineNumber -> rowErrors.add(new CsvRowValidationCheckResult(new MissingParentLineValidationCheckResult(lineNumber, refType, missingParentReference, referenceUUIDs.keySet()), lineNumber)) + ); + }); + } return collect.stream(); } @@ -1525,9 +1500,15 @@ public class OreSiService { Datum datum; } + @Value + private static class RowWithReferenceDatum { + int lineNumber; + ReferenceDatum referenceDatum; + } + @Value private static class ParsedCsvRow { int lineNumber; List<Map.Entry<String, String>> columns; } -} \ No newline at end of file +} diff --git a/src/main/java/fr/inra/oresing/checker/DateValidationCheckResult.java b/src/main/java/fr/inra/oresing/rest/validationcheckresults/DateValidationCheckResult.java similarity index 94% rename from src/main/java/fr/inra/oresing/checker/DateValidationCheckResult.java rename to src/main/java/fr/inra/oresing/rest/validationcheckresults/DateValidationCheckResult.java index 66d533df785a7bf97c607f7f82ff6a58368d2471..8bf361a4f9a4d3445b4af60f24e80c6c43b32a5a 100644 --- a/src/main/java/fr/inra/oresing/checker/DateValidationCheckResult.java +++ b/src/main/java/fr/inra/oresing/rest/validationcheckresults/DateValidationCheckResult.java @@ -1,7 +1,9 @@ -package fr.inra.oresing.checker; +package fr.inra.oresing.rest.validationcheckresults; import com.google.common.collect.ImmutableMap; import fr.inra.oresing.ValidationLevel; +import fr.inra.oresing.checker.CheckerTarget; +import fr.inra.oresing.checker.DateLineChecker; import fr.inra.oresing.rest.ValidationCheckResult; import lombok.Value; diff --git a/src/main/java/fr/inra/oresing/rest/DefaultValidationCheckResult.java b/src/main/java/fr/inra/oresing/rest/validationcheckresults/DefaultValidationCheckResult.java similarity index 90% rename from src/main/java/fr/inra/oresing/rest/DefaultValidationCheckResult.java rename to src/main/java/fr/inra/oresing/rest/validationcheckresults/DefaultValidationCheckResult.java index cbb109f950a4fa3fc5756d5887744e40d307384c..66f227a2d934647598cc3fc4a2822de0d1fc0029 100644 --- a/src/main/java/fr/inra/oresing/rest/DefaultValidationCheckResult.java +++ b/src/main/java/fr/inra/oresing/rest/validationcheckresults/DefaultValidationCheckResult.java @@ -1,7 +1,8 @@ -package fr.inra.oresing.rest; +package fr.inra.oresing.rest.validationcheckresults; import com.google.common.collect.ImmutableMap; import fr.inra.oresing.ValidationLevel; +import fr.inra.oresing.rest.ValidationCheckResult; import lombok.Value; import java.util.Map; diff --git a/src/main/java/fr/inra/oresing/rest/validationcheckresults/DuplicationLineValidationCheckResult.java b/src/main/java/fr/inra/oresing/rest/validationcheckresults/DuplicationLineValidationCheckResult.java new file mode 100644 index 0000000000000000000000000000000000000000..75f3606e5c759277b0e3c9a140f21440cc2404a1 --- /dev/null +++ b/src/main/java/fr/inra/oresing/rest/validationcheckresults/DuplicationLineValidationCheckResult.java @@ -0,0 +1,52 @@ +package fr.inra.oresing.rest.validationcheckresults; + +import com.google.common.collect.ImmutableMap; +import fr.inra.oresing.ValidationLevel; +import fr.inra.oresing.persistence.Ltree; +import fr.inra.oresing.rest.ValidationCheckResult; + +import java.util.List; +import java.util.Map; + +public class DuplicationLineValidationCheckResult implements ValidationCheckResult { + + public static String MESSAGE_FOR_REFERENCES ="duplicatedLineInReference"; + public static String MESSAGE_FOR_DATATYPES ="duplicatedLineInDatatype"; + ValidationLevel level; + String message; + + Map<String, Object> messageParams; + + public DuplicationLineValidationCheckResult(FileType filetype, String file, ValidationLevel level, Ltree hierarchicalKey, int currentLineNumber, List<Integer> otherLines) { + this.level = level; + this.message = FileType.DATATYPE.message; + this.messageParams = ImmutableMap.of( + "file", file, + "lineNumber", currentLineNumber, + "otherLines", otherLines, + "duplicateKey", hierarchicalKey.getSql() + ); + } + + @Override + public ValidationLevel getLevel() { + return level; + } + + @Override + public String getMessage() { + return message; + } + + @Override + public Map<String, Object> getMessageParams() { + return this.messageParams; + } + public enum FileType{ + DATATYPE(MESSAGE_FOR_DATATYPES),REFERENCES(MESSAGE_FOR_REFERENCES); + String message; + FileType(String message) { + this.message = message; + } + }; +} \ No newline at end of file diff --git a/src/main/java/fr/inra/oresing/rest/validationcheckresults/MissingParentLineValidationCheckResult.java b/src/main/java/fr/inra/oresing/rest/validationcheckresults/MissingParentLineValidationCheckResult.java new file mode 100644 index 0000000000000000000000000000000000000000..a4723a3ef0b1adaf3add28e88182260882fc8d7f --- /dev/null +++ b/src/main/java/fr/inra/oresing/rest/validationcheckresults/MissingParentLineValidationCheckResult.java @@ -0,0 +1,38 @@ +package fr.inra.oresing.rest.validationcheckresults; + +import com.google.common.collect.ImmutableMap; +import fr.inra.oresing.ValidationLevel; +import fr.inra.oresing.rest.ValidationCheckResult; + +import java.util.Map; +import java.util.Set; + +public class MissingParentLineValidationCheckResult implements ValidationCheckResult { + public static String MISSING_PARENT_LINE_IN_RECURSIVE_REFERENCE ="missingParentLineInRecursiveReference"; + Map<String, Object> messageParams; + + public MissingParentLineValidationCheckResult(long lineNumber, String refType, String missingReferencesKey, Set<String> knownReferences ) { + super(); + this.messageParams = ImmutableMap.of( + "lineNumber", lineNumber, + "references", refType, + "missingReferencesKey", missingReferencesKey, + "knownReferences", knownReferences + ); + } + + @Override + public ValidationLevel getLevel() { + return ValidationLevel.ERROR; + } + + @Override + public String getMessage() { + return MISSING_PARENT_LINE_IN_RECURSIVE_REFERENCE; + } + + @Override + public Map<String, Object> getMessageParams() { + return this.messageParams; + } +} \ No newline at end of file diff --git a/src/main/java/fr/inra/oresing/checker/ReferenceValidationCheckResult.java b/src/main/java/fr/inra/oresing/rest/validationcheckresults/ReferenceValidationCheckResult.java similarity index 90% rename from src/main/java/fr/inra/oresing/checker/ReferenceValidationCheckResult.java rename to src/main/java/fr/inra/oresing/rest/validationcheckresults/ReferenceValidationCheckResult.java index a565f433151a73e799169c543951b3ca55bc0eed..1af149b85acefbe1b65fb4cb0c0157a7fc282929 100644 --- a/src/main/java/fr/inra/oresing/checker/ReferenceValidationCheckResult.java +++ b/src/main/java/fr/inra/oresing/rest/validationcheckresults/ReferenceValidationCheckResult.java @@ -1,7 +1,8 @@ -package fr.inra.oresing.checker; +package fr.inra.oresing.rest.validationcheckresults; import com.google.common.collect.ImmutableMap; import fr.inra.oresing.ValidationLevel; +import fr.inra.oresing.checker.CheckerTarget; import fr.inra.oresing.rest.ValidationCheckResult; import lombok.Value; diff --git a/src/test/java/fr/inra/oresing/rest/Fixtures.java b/src/test/java/fr/inra/oresing/rest/Fixtures.java index 377615bba489b574fd5e6a7053c65884feefccf1..f1cfa32968db2920aea8c83833fe859e7f8bf159 100644 --- a/src/test/java/fr/inra/oresing/rest/Fixtures.java +++ b/src/test/java/fr/inra/oresing/rest/Fixtures.java @@ -468,6 +468,26 @@ public class Fixtures { return "/data/pros/donnees_prelevement_pro.csv"; } + public String getDuplicatedApplicationConfigurationResourceName() { + return "/data/duplication/duplication.yaml"; + } + + public Map<String, String> getDuplicatedReferentielFiles() { + Map<String, String> referentielFiles = new LinkedHashMap<>(); + referentielFiles.put("typezonewithoutduplication", "/data/duplication/typezone.csv"); + referentielFiles.put("typezonewithduplication", "/data/duplication/typezoneduplique.csv"); + referentielFiles.put("zonewithoutduplication", "/data/duplication/zone_etude.csv"); + referentielFiles.put("zonewithduplication", "/data/duplication/zone_etude_dupliqué.csv"); + referentielFiles.put("zonewithmissingparent", "/data/duplication/zone_etude_missing_parent.csv"); + return referentielFiles; + } + + public Map<String, String> getDuplicatedDataFiles() { + Map<String, String> referentielFiles = new LinkedHashMap<>(); + referentielFiles.put("data_without_duplicateds", "/data/duplication/data.csv"); + return referentielFiles; + } + public String getPhysicoChimieSolsProDataResourceName() { return "/data/pros/physico_chimie_sols.csv"; } diff --git a/src/test/java/fr/inra/oresing/rest/OreSiResourcesTest.java b/src/test/java/fr/inra/oresing/rest/OreSiResourcesTest.java index e645b42ddfc22a6c293f3b85912a3252f5474f04..4f329573cbe8d46c995d7892fcd0c14966c9f10e 100644 --- a/src/test/java/fr/inra/oresing/rest/OreSiResourcesTest.java +++ b/src/test/java/fr/inra/oresing/rest/OreSiResourcesTest.java @@ -6,6 +6,8 @@ import com.google.common.io.Resources; import com.jayway.jsonpath.JsonPath; import fr.inra.oresing.OreSiNg; import fr.inra.oresing.OreSiTechnicalException; +import fr.inra.oresing.ValidationLevel; +import fr.inra.oresing.checker.InvalidDatasetContentException; import fr.inra.oresing.persistence.AuthenticationService; import fr.inra.oresing.persistence.JsonRowMapper; import lombok.extern.slf4j.Slf4j; @@ -13,6 +15,7 @@ import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.hamcrest.Matchers; import org.hamcrest.core.Is; +import org.hamcrest.core.IsEqual; import org.hamcrest.core.IsNull; import org.json.JSONArray; import org.junit.Assert; @@ -36,6 +39,7 @@ import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; +import org.springframework.web.util.NestedServletException; import javax.servlet.http.Cookie; import java.io.IOException; @@ -899,6 +903,233 @@ public class OreSiResourcesTest { } } + @Test + public void addDuplicatedTest() throws Exception { + authenticationService.addUserRightCreateApplication(userId); + try (InputStream configurationFile = fixtures.getClass().getResourceAsStream(fixtures.getDuplicatedApplicationConfigurationResourceName())) { + MockMultipartFile configuration = new MockMultipartFile("file", "duplicated.yaml", "text/plain", configurationFile); + mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/duplicated") + .file(configuration) + .cookie(authCookie)) + .andExpect(MockMvcResultMatchers.status().is2xxSuccessful()); + } + String message; + + //on charge le fichier de type zone d'étude + final String typezonewithoutduplicationDuplication = fixtures.getDuplicatedReferentielFiles().get("typezonewithoutduplication"); + try (InputStream refStream = fixtures.getClass().getResourceAsStream(typezonewithoutduplicationDuplication)) { + MockMultipartFile refFile = new MockMultipartFile("file", "type_zone_etude.csv", "text/plain", refStream); + message = mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/duplicated/references/{refType}", "types_de_zones_etudes") + .file(refFile) + .cookie(authCookie)) + .andExpect(status().is2xxSuccessful()) + .andReturn().getResponse().getContentAsString(); + } + + // on vérifie le nombre de ligne + message = mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/applications/duplicated/references/{refType}", "types_de_zones_etudes") + .cookie(authCookie)) + .andExpect(status().is2xxSuccessful()) + .andExpect(jsonPath("$.referenceValues.length()", IsEqual.equalTo(2))) + .andReturn().getResponse().getContentAsString(); + + + //on recharge le fichier de type zone d'étude + try (InputStream refStream = fixtures.getClass().getResourceAsStream(typezonewithoutduplicationDuplication)) { + MockMultipartFile refFile = new MockMultipartFile("file", "type_zone_etude2.csv", "text/plain", refStream); + message = mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/duplicated/references/{refType}", "types_de_zones_etudes") + .file(refFile) + .cookie(authCookie)) + .andExpect(status().is2xxSuccessful()) + .andReturn().getResponse().getContentAsString(); + } + + //il doit toujours y avoir le même nombre de ligne + message = mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/applications/duplicated/references/{refType}", "types_de_zones_etudes") + .cookie(authCookie)) + .andExpect(status().is2xxSuccessful()) + .andExpect(jsonPath("$.referenceValues.length()", IsEqual.equalTo(2))) + .andReturn().getResponse().getContentAsString(); + + + //on charge le fichier de zone type d'étude avec une duplication + final String typezonewithduplicationDuplication = fixtures.getDuplicatedReferentielFiles().get("typezonewithduplication"); + try (InputStream refStream = fixtures.getClass().getResourceAsStream(typezonewithduplicationDuplication)) { + MockMultipartFile refFile = new MockMultipartFile("file", "type_zone_etude_duplicate.csv", "text/plain", refStream); + final ResultActions error = mockMvc + .perform(MockMvcRequestBuilders.multipart("/api/v1/applications/duplicated/references/{refType}", "types_de_zones_etudes") + .file(refFile) + .cookie(authCookie)); + Assert.fail(); + } catch (NestedServletException e) { + Assert.assertTrue(e.getCause() instanceof InvalidDatasetContentException); + final InvalidDatasetContentException invalidDatasetContentException = (InvalidDatasetContentException) e.getCause(); + final List<CsvRowValidationCheckResult> errors = invalidDatasetContentException.getErrors(); + Assert.assertEquals(1, errors.size()); + Assert.assertEquals(4, errors.get(0).getLineNumber()); + final ValidationCheckResult validationCheckResult = errors.get(0).getValidationCheckResult(); + Assert.assertEquals(ValidationLevel.ERROR, validationCheckResult.getLevel()); + Assert.assertEquals("duplicatedLineInDatatype", validationCheckResult.getMessage()); + final Map<String, Object> messageParams = validationCheckResult.getMessageParams(); + Assert.assertEquals("types_de_zones_etudes", messageParams.get("file")); + Assert.assertEquals(4, messageParams.get("lineNumber")); + Assert.assertArrayEquals(new Integer[]{3, 4}, ((List) messageParams.get("otherLines")).toArray()); + Assert.assertEquals("zone20", messageParams.get("duplicateKey")); + } + + //il doit toujours y avoir le même nombre de ligne + message = mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/applications/duplicated/references/{refType}", "types_de_zones_etudes") + .cookie(authCookie)) + .andExpect(status().is2xxSuccessful()) + .andExpect(jsonPath("$.referenceValues.length()", IsEqual.equalTo(2))) + .andReturn().getResponse().getContentAsString(); +/* +on test le dépôt d'un fichier récursif + */ + + +//on charge le fichier de zone d'étude + final String zonewithoutduplicationDuplication = fixtures.getDuplicatedReferentielFiles().get("zonewithoutduplication"); + try (InputStream refStream = fixtures.getClass().getResourceAsStream(zonewithoutduplicationDuplication)) { + MockMultipartFile refFile = new MockMultipartFile("file", "zone_etude.csv", "text/plain", refStream); + message = mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/duplicated/references/{refType}", "zones_etudes") + .file(refFile) + .cookie(authCookie)) + .andExpect(status().is2xxSuccessful()) + .andReturn().getResponse().getContentAsString(); + } + + // on vérifie le nombre de ligne + message = mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/applications/duplicated/references/{refType}", "zones_etudes") + .cookie(authCookie)) + .andExpect(status().is2xxSuccessful()) + .andExpect(jsonPath("$.referenceValues.length()", IsEqual.equalTo(2))) + .andReturn().getResponse().getContentAsString(); + + + //on recharge le fichier de zone d'étude + try (InputStream refStream = fixtures.getClass().getResourceAsStream(zonewithoutduplicationDuplication)) { + MockMultipartFile refFile = new MockMultipartFile("file", "zone_etude2.csv", "text/plain", refStream); + message = mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/duplicated/references/{refType}", "zones_etudes") + .file(refFile) + .cookie(authCookie)) + .andExpect(status().is2xxSuccessful()) + .andReturn().getResponse().getContentAsString(); + } + + //il doit toujours y avoir le même nombre de ligne + message = mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/applications/duplicated/references/{refType}", "zones_etudes") + .cookie(authCookie)) + .andExpect(status().is2xxSuccessful()) + .andExpect(jsonPath("$.referenceValues.length()", IsEqual.equalTo(2))) + .andReturn().getResponse().getContentAsString(); + + + //on charge le fichier de zone d'étudeavec une duplication + final String zonewithduplicationDuplication = fixtures.getDuplicatedReferentielFiles().get("zonewithduplication"); + try (InputStream refStream = fixtures.getClass().getResourceAsStream(zonewithduplicationDuplication)) { + MockMultipartFile refFile = new MockMultipartFile("file", "zone_etude_duplicated.csv", "text/plain", refStream); + final ResultActions error = mockMvc + .perform(MockMvcRequestBuilders.multipart("/api/v1/applications/duplicated/references/{refType}", "zones_etudes") + .file(refFile) + .cookie(authCookie)); + Assert.fail(); + } catch (NestedServletException e) { + Assert.assertTrue(e.getCause() instanceof InvalidDatasetContentException); + final InvalidDatasetContentException invalidDatasetContentException = (InvalidDatasetContentException) e.getCause(); + final List<CsvRowValidationCheckResult> errors = invalidDatasetContentException.getErrors(); + Assert.assertEquals(1, errors.size()); + Assert.assertEquals(4, errors.get(0).getLineNumber()); + final ValidationCheckResult validationCheckResult = errors.get(0).getValidationCheckResult(); + Assert.assertEquals(ValidationLevel.ERROR, validationCheckResult.getLevel()); + Assert.assertEquals("duplicatedLineInDatatype", validationCheckResult.getMessage()); + final Map<String, Object> messageParams = validationCheckResult.getMessageParams(); + Assert.assertEquals("zones_etudes", messageParams.get("file")); + Assert.assertEquals(4, messageParams.get("lineNumber")); + Assert.assertArrayEquals(new Integer[]{2, 4}, ((List) messageParams.get("otherLines")).toArray()); + Assert.assertEquals("site1", messageParams.get("duplicateKey")); + } + + //il doit toujours y avoir le même nombre de ligne + message = mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/applications/duplicated/references/{refType}", "zones_etudes") + .cookie(authCookie)) + .andExpect(status().is2xxSuccessful()) + .andExpect(jsonPath("$.referenceValues.length()", IsEqual.equalTo(2))) + .andReturn().getResponse().getContentAsString(); + + //on charge le fichier de zone d'étudeavec une duplication + final String zonewithmissingParent = fixtures.getDuplicatedReferentielFiles().get("zonewithmissingparent"); + try (InputStream refStream = fixtures.getClass().getResourceAsStream(zonewithmissingParent)) { + MockMultipartFile refFile = new MockMultipartFile("file", "zone_etude_missing_parent.csv", "text/plain", refStream); + final ResultActions error = mockMvc + .perform(MockMvcRequestBuilders.multipart("/api/v1/applications/duplicated/references/{refType}", "zones_etudes") + .file(refFile) + .cookie(authCookie)); + Assert.fail(); + } catch (NestedServletException e) { + Assert.assertTrue(e.getCause() instanceof InvalidDatasetContentException); + final InvalidDatasetContentException invalidDatasetContentException = (InvalidDatasetContentException) e.getCause(); + final List<CsvRowValidationCheckResult> errors = invalidDatasetContentException.getErrors(); + Assert.assertEquals(1, errors.size()); + Assert.assertEquals(3, errors.get(0).getLineNumber()); + final ValidationCheckResult validationCheckResult = errors.get(0).getValidationCheckResult(); + Assert.assertEquals(ValidationLevel.ERROR, validationCheckResult.getLevel()); + Assert.assertEquals("missingParentLineInRecursiveReference", validationCheckResult.getMessage()); + final Map<String, Object> messageParams = validationCheckResult.getMessageParams(); + Assert.assertEquals("zones_etudes", messageParams.get("references")); + Assert.assertEquals(3L, messageParams.get("lineNumber")); + Assert.assertEquals("site3", messageParams.get("missingReferencesKey")); + Assert.assertTrue(Set.of("site3","site1.site2","site1","site2").containsAll((Set) messageParams.get("knownReferences"))); + } + + //il doit toujours y avoir le même nombre de ligne + message = mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/applications/duplicated/references/{refType}", "zones_etudes") + .cookie(authCookie)) + .andExpect(status().is2xxSuccessful()) + .andExpect(jsonPath("$.referenceValues.length()", IsEqual.equalTo(2))) + .andReturn().getResponse().getContentAsString(); + + //on teste un dépot de fichier de données + final String dataWithoutDuplicateds = fixtures.getDuplicatedDataFiles().get("data_without_duplicateds"); + try (InputStream refStream = fixtures.getClass().getResourceAsStream(dataWithoutDuplicateds)) { + MockMultipartFile refFile = new MockMultipartFile("file", "data_without_duplicateds.csv", "text/plain", refStream); + mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/duplicated/data/dty") + .file(refFile) + .cookie(authCookie)) + .andExpect(MockMvcResultMatchers.status().is2xxSuccessful()); + } + //on teste le nombre de ligne + try (InputStream refStream = fixtures.getClass().getResourceAsStream(dataWithoutDuplicateds)) { + final String response = mockMvc.perform(get("/api/v1/applications/duplicated/data/dty") + .cookie(authCookie)) + .andExpect(status().is2xxSuccessful()) + .andExpect(jsonPath("$.totalRows", IsEqual.equalTo(4 + ))) + .andReturn().getResponse().getContentAsString(); + log.debug(response); + } + + // on redepose le fichier + + try (InputStream refStream = fixtures.getClass().getResourceAsStream(dataWithoutDuplicateds)) { + MockMultipartFile refFile = new MockMultipartFile("file", "data_without_duplicateds.csv", "text/plain", refStream); + mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/duplicated/data/dty") + .file(refFile) + .cookie(authCookie)) + .andExpect(MockMvcResultMatchers.status().is2xxSuccessful()); + } + // le nombre de ligne est inchangé + /*try (InputStream refStream = fixtures.getClass().getResourceAsStream(dataWithoutDuplicateds)) { + final String response = mockMvc.perform(get("/api/v1/applications/duplicated/data/dty") + .cookie(authCookie)) + .andExpect(status().is2xxSuccessful()) + .andExpect(jsonPath("$.totalRows", IsEqual.equalTo(4 + ))) + .andReturn().getResponse().getContentAsString(); + log.debug(response); + }*/ + } + @Test public void addApplicationOLAC() throws Exception { authenticationService.addUserRightCreateApplication(userId); @@ -1012,7 +1243,7 @@ public class OreSiResourcesTest { for (Map.Entry<String, String> entry : fixtures.getFluxMeteoForetEssaiDataResourceName().entrySet()) { try (InputStream refStream = fixtures.getClass().getResourceAsStream(entry.getValue())) { MockMultipartFile refFile = new MockMultipartFile("file", "flux_meteo_dataResult.csv", "text/plain", refStream); - mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/foret/data/"+entry.getKey()) + mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/foret/data/" + entry.getKey()) .file(refFile) .cookie(authCookie)) .andExpect(MockMvcResultMatchers.status().is2xxSuccessful()); diff --git a/src/test/resources/data/duplication/data.csv b/src/test/resources/data/duplication/data.csv new file mode 100644 index 0000000000000000000000000000000000000000..250c8f3114d79f262d32b7c155ebfe71ee647b46 --- /dev/null +++ b/src/test/resources/data/duplication/data.csv @@ -0,0 +1,5 @@ +site;date +site1.site2;23/02/1980 +site1.site2;24/02/1980 +site1;23/02/1980 +site1;24/02/1980 \ No newline at end of file diff --git a/src/test/resources/data/duplication/duplication.yaml b/src/test/resources/data/duplication/duplication.yaml new file mode 100644 index 0000000000000000000000000000000000000000..cae0e427ef5054a09cbd3f3306a02cd446a6b8fd --- /dev/null +++ b/src/test/resources/data/duplication/duplication.yaml @@ -0,0 +1,72 @@ +version: 0 +application: + name: duplication + version: 1 +compositeReferences: + localizations: + components: + - reference: zones_etudes + parentRecursiveKey: parent +references: + types_de_zones_etudes: + keyColumns: [nom] + columns: + nom: + zones_etudes: + validations: + parent_ref: + description: référence au parent + checker: + name: Reference + params: + refType: zones_etudes + columns: parent + required: false + codify: true + keyColumns: [nom] + columns: + nom: + parent: +dataTypes: + dty: + authorization: + dataGroups: + reference: + data: + - localization + - Date + label: "Reference" + authorizationScopes: + authorization_zoneEtude: + component: zones_etudes + variable: localization + timeScope: + component: day + variable: Date + data: + Date: + components: + day: + checker: + name: Date + params: + pattern: dd/MM/yyyy + localization: + components: + zones_etudes: + checker: + name: Reference + params: + refType: zones_etudes + format: + headerLine: 1 + firstRowLine: 2 + columns: + - header: "site" + boundTo: + variable: localization + component: zones_etudes + - header: "date" + boundTo: + variable: Date + component: day \ No newline at end of file diff --git a/src/test/resources/data/duplication/typezone.csv b/src/test/resources/data/duplication/typezone.csv new file mode 100644 index 0000000000000000000000000000000000000000..7372a66a40d3f56f2ef919953758b872436ab8ab --- /dev/null +++ b/src/test/resources/data/duplication/typezone.csv @@ -0,0 +1,3 @@ +nom; +zone1; +zone2; \ No newline at end of file diff --git a/src/test/resources/data/duplication/typezoneduplique.csv b/src/test/resources/data/duplication/typezoneduplique.csv new file mode 100644 index 0000000000000000000000000000000000000000..a9be259d5e6302c06f5437d7da6170c844dcc0a1 --- /dev/null +++ b/src/test/resources/data/duplication/typezoneduplique.csv @@ -0,0 +1,5 @@ +nom; +zone10; +zone20; +zone20; +zone30; \ No newline at end of file diff --git a/src/test/resources/data/duplication/zone_etude.csv b/src/test/resources/data/duplication/zone_etude.csv new file mode 100644 index 0000000000000000000000000000000000000000..10a42bca849fc8e95f8ac3fe8460af7b681b9100 --- /dev/null +++ b/src/test/resources/data/duplication/zone_etude.csv @@ -0,0 +1,3 @@ +nom;parent; +site1;; +site2;site1; \ No newline at end of file diff --git "a/src/test/resources/data/duplication/zone_etude_dupliqu\303\251.csv" "b/src/test/resources/data/duplication/zone_etude_dupliqu\303\251.csv" new file mode 100644 index 0000000000000000000000000000000000000000..cc4f7bf7b09df0d3e5d54c74e956fdd406f15b16 --- /dev/null +++ "b/src/test/resources/data/duplication/zone_etude_dupliqu\303\251.csv" @@ -0,0 +1,4 @@ +nom;parent; +site1;; +site2;site1; +site1;; \ No newline at end of file diff --git a/src/test/resources/data/duplication/zone_etude_missing_parent.csv b/src/test/resources/data/duplication/zone_etude_missing_parent.csv new file mode 100644 index 0000000000000000000000000000000000000000..b0bdde17e050dd6f9cc2c2f557063c37898b2683 --- /dev/null +++ b/src/test/resources/data/duplication/zone_etude_missing_parent.csv @@ -0,0 +1,4 @@ +nom;parent; +site1;; +site1;site3; +site2;site1; \ No newline at end of file diff --git a/ui2/src/locales/en.json b/ui2/src/locales/en.json index bf7cfbfe2f1172afc8863e2dd3743186fee822df..4084364098bac84b069d968719c8f9662ec3f846 100644 --- a/ui2/src/locales/en.json +++ b/ui2/src/locales/en.json @@ -158,7 +158,10 @@ "requiredValueWithColumn": "For column: <code> {target} </code> the value cannot be zero.", "timerangeoutofinterval":"The date <code> {value} </code> is incompatible with the date range of the deposit. It must be between <code> {from} </code> and <code> {to} </code>. ", "badauthorizationscopeforrepository":"Authorization <code> {authorization} </code>) must be <code> {expectedValue} / code> and not <code> {givenValue} </code>", - "overlappingpublishedversion":"There is a deposited version in the deposit dates have an overlapping period with the deposited version. <code> {overlapingFiles] </code>" + "overlappingpublishedversion":"There is a deposited version in the deposit dates have an overlapping period with the deposited version. <code> {overlapingFiles] </code>", + "duplicateLineInReference": "In the repository file {file}, line {lineNumber} has the same id {duplicateKey} as lines {otherLines}", + "duplicateLineInDatatype": "In data file {file}, line {lineNumber} has the same identifier {duplicateKey} as lines {otherLines}", + "missingParentLineInRecursiveReference": "In repository file {references} the id value {missingReferencesKey} for parent does not exist. Accepted values ${knownReferences}" }, "referencesManagement":{ "actions":"Actions", @@ -245,4 +248,4 @@ "regEx": ".*", "star": "*" } -} \ No newline at end of file +} diff --git a/ui2/src/locales/fr.json b/ui2/src/locales/fr.json index ea2ef42d1c8892af8392eb88f03a8d60010da83d..48ea91b1b508fd3c070fb6335560d090db303f5a 100644 --- a/ui2/src/locales/fr.json +++ b/ui2/src/locales/fr.json @@ -158,7 +158,10 @@ "requiredValueWithColumn": "Pour la colonne : <code>{target}</code> la valeur ne peut pas être nulle.", "timerangeoutofinterval": "La date <code>{value}</code> est incompatible avec l'intervale de dates du dépôt. Elle doit être comprise entre <code>{from}</code> et <code>{to}</code>.", "badauthorizationscopeforrepository": "L'autorisation <code>{authorization}</code>) doit être <code>{expectedValue}/code> et non <code>{givenValue}</code>", - "overlappingpublishedversion": "Il existe une version déposée dans les dates de dépôt ont une période chevauchante avec la version déposée. <code>{overlapingFiles]</code>" + "overlappingpublishedversion": "Il existe une version déposée dans les dates de dépôt ont une période chevauchante avec la version déposée. <code>{overlapingFiles]</code>", + "duplicatedLineInReference": "Dans le fichier du référentiel {file}, la ligne {lineNumber} a le même identifiant {duplicateKey} que les lignes {otherLines}", + "duplicatedLineInDatatype": "Dans le fichier de données {file}, la ligne {lineNumber} a le même identifiant {duplicateKey} que les lignes {otherLines}", + "missingParentLineInRecursiveReference": "Dans le fichier du référentiel {references} la valeur d'identifiant {missingReferencesKey} pour le parent n'existe pas. Valeurs acceptées {knownReferences}" }, "referencesManagement": { "actions": "Actions", @@ -244,4 +247,4 @@ "slash": "/", "regEx": ".*" } -} \ No newline at end of file +}