Resolve RED-8339 "4" #511

Merged
ali.oezyetimoglu1 merged 8 commits from RED-8339-4 into master 2024-06-03 15:38:31 +02:00
36 changed files with 2026 additions and 850 deletions

View File

@ -3,30 +3,22 @@ package com.iqser.red.persistence.service.v1.external.api.impl.controller;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.GET_RSS;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.READ_REDACTION_LOG;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
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.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
import com.iqser.red.service.persistence.management.v1.processor.service.AccessControlService;
import com.iqser.red.service.persistence.management.v1.processor.service.ComponentLogService;
import com.iqser.red.service.persistence.management.v1.processor.service.ComponentOverrideService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
import com.iqser.red.service.persistence.service.v1.api.external.resource.ComponentLogResource;
import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLog;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntry;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntryValue;
import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentsOverrides;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentOverrideList;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.RevertOverrideRequest;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
@ -35,15 +27,15 @@ import lombok.experimental.FieldDefaults;
@RestController
@RequiredArgsConstructor
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
@Deprecated(forRemoval = true)
public class ComponentLogController implements ComponentLogResource {
ComponentLogService componentLogService;
ComponentOverrideService componentOverrideService;
AuditPersistenceService auditPersistenceService;
AccessControlService accessControlService;
@Override
@Deprecated(forRemoval = true)
@PreAuthorize("hasAuthority('" + READ_REDACTION_LOG + "')")
public ComponentLog getComponentLog(String dossierId, String fileId, boolean includeOverrides) {
@ -54,134 +46,47 @@ public class ComponentLogController implements ComponentLogResource {
}
@PostMapping(value = COMPONENT_LOG_PATH + OVERRIDE_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
public void addOverrides(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody ComponentsOverrides componentsOverrides) {
public void addOverride(@RequestParam(name = "dossierTemplateId") String dossierTemplateId,
@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody ComponentLogEntry override) {
accessControlService.checkDossierExistenceAndAccessPermissionsToDossier(dossierId);
accessControlService.validateFileResourceExistence(fileId);
if (componentsOverrides.getComponentOverrides() == null || componentsOverrides.getComponentOverrides().isEmpty()) {
throw new BadRequestException("Request body cannot be empty!");
}
var componentLog = componentLogService.getComponentLog(dossierId, fileId);
var allComponents = componentLog.getComponentLogEntries();
componentLogService.addOverride(dossierId, fileId, override);
componentOverrideService.addOverrides(dossierId, fileId, componentsOverrides);
componentsOverrides.getComponentOverrides()
.forEach((componentName, overrideValue) -> auditOverride(dossierId, fileId, componentName, overrideValue, allComponents));
}
@GetMapping(value = COMPONENT_LOG_PATH + OVERRIDE_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
public ComponentsOverrides getOverrides(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId) {
public ComponentOverrideList getOverrides(@RequestParam(name = "dossierTemplateId") String dossierTemplateId,
@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId) {
accessControlService.checkDossierExistenceAndViewPermissionsToDossier(dossierId);
accessControlService.validateFileResourceExistence(fileId);
return componentOverrideService.getOverrides(dossierId, fileId);
var overrides = componentLogService.getOverrides(dossierId, fileId);
return ComponentOverrideList.builder().componentOverrides(overrides).build();
}
@PostMapping(value = COMPONENT_LOG_PATH + OVERRIDE_PATH + "/revert" + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
public void revertOverrides(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody RevertOverrideRequest revertOverrideRequest) {
public void revertOverrides(@RequestParam(name = "dossierTemplateId") String dossierTemplateId,
@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody RevertOverrideRequest revertOverrideRequest) {
accessControlService.checkDossierExistenceAndAccessPermissionsToDossier(dossierId);
accessControlService.validateFileResourceExistence(fileId);
if (revertOverrideRequest.getComponents() == null || revertOverrideRequest.getComponents().isEmpty()) {
throw new BadRequestException("Request body cannot be empty!");
}
var componentLog = componentLogService.getComponentLog(dossierId, fileId);
var allComponents = componentLog.getComponentLogEntries();
componentOverrideService.revertOverrides(dossierId, fileId, revertOverrideRequest);
revertOverrideRequest.getComponents()
.forEach(componentNameToRevert -> auditOverrideRevert(dossierId, fileId, componentNameToRevert, allComponents));
}
private void auditOverride(String dossierId, String fileId, String componentName, String overrideValue, List<ComponentLogEntry> allComponentLogEntries) {
Optional<ComponentLogEntry> component = allComponentLogEntries.stream()
.filter(c -> c.getName().equals(componentName))
.findFirst();
String originalValue = getOriginalValue(component);
String value = getValue(component);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("The component is overwritten with value")
.details(Map.of(DOSSIER_ID,
dossierId,
FILE_ID,
fileId,
"ComponentName",
componentName,
"Action",
"MODIFY",
"OriginalValue",
originalValue,
"OldValue",
value,
"NewValue",
overrideValue))
.build());
}
private void auditOverrideRevert(String dossierId, String fileId, String componentNameToRevert, List<ComponentLogEntry> allComponentLogEntries) {
Optional<ComponentLogEntry> component = allComponentLogEntries.stream()
.filter(c -> c.getName().equals(componentNameToRevert))
.findFirst();
String originalValue = getOriginalValue(component);
String value = getValue(component);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("The component override for was reverted")
.details(Map.of(DOSSIER_ID,
dossierId,
FILE_ID,
fileId,
"ComponentName",
componentNameToRevert,
"Action",
"REVERT",
"OriginalValue",
originalValue,
"OldValue",
value,
"NewValue",
originalValue))
.build());
}
private String getValue(Optional<ComponentLogEntry> component) {
return component.map(ComponentLogEntry::getComponentValues)
.stream()
.map(a -> a.stream()
.map(ComponentLogEntryValue::getValue)
.collect(Collectors.joining(", ")))
.findFirst()
.orElse("");
}
private static String getOriginalValue(Optional<ComponentLogEntry> component) {
return component.map(ComponentLogEntry::getComponentValues)
.stream()
.map(a -> a.stream()
.map(ComponentLogEntryValue::getOriginalValue)
.collect(Collectors.joining(", ")))
.findFirst()
.orElse("");
componentLogService.revertOverrides(dossierId, fileId, revertOverrideRequest);
}
}

View File

@ -1,287 +0,0 @@
package com.iqser.red.persistence.service.v1.external.api.impl.controller;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.GET_RSS;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.iqser.red.service.persistence.management.v1.processor.service.ComponentLogService;
import com.iqser.red.service.persistence.management.v1.processor.service.ComponentOverrideService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
import com.iqser.red.service.persistence.service.v1.api.external.resource.RSSResource;
import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntityReference;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntry;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntryValue;
import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentsOverrides;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.RevertOverrideRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.rss.RSSFileResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.rss.RSSResponse;
import com.iqser.red.service.redaction.report.v1.api.model.rss.DetailedRSSFileResponse;
import com.iqser.red.service.redaction.report.v1.api.model.rss.DetailedRSSResponse;
import com.iqser.red.service.redaction.report.v1.api.model.rss.SCMComponent;
import com.iqser.red.service.redaction.report.v1.api.model.rss.ScmAnnotation;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import lombok.RequiredArgsConstructor;
@Deprecated(forRemoval = true)
@RestController
@RequiredArgsConstructor
@ConditionalOnProperty(name = "application.rss.component-log.enabled", havingValue = "true")
public class RSSComponentLogController implements RSSResource {
private final ComponentOverrideService componentOverrideService;
private final AuditPersistenceService auditPersistenceService;
private final ComponentLogService componentLogService;
private final StatusController statusController;
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
public RSSResponse getRSS(@PathVariable(DOSSIER_ID) String dossierId, @RequestParam(value = "fileId", required = false) String fileId) {
List<FileStatus> dossierFiles;
if (StringUtils.isBlank(fileId)) {
dossierFiles = statusController.getDossierStatus(dossierId);
} else {
dossierFiles = List.of(statusController.getFileStatus(dossierId, fileId));
}
List<RSSFileResponse> fileResponses = dossierFiles.stream()
.map(this::getRssResponse)
.toList();
return new RSSResponse(fileResponses);
}
private RSSFileResponse getRssResponse(FileStatus file) {
var componentLog = componentLogService.getComponentLog(file.getDossierId(), file.getId(), true);
Map<String, String> results = new LinkedHashMap<>();
componentLog.getComponentLogEntries()
.forEach(entry -> {
if (entry.getComponentValues().size() <= 1) {
results.put(entry.getName(),
entry.getComponentValues()
.get(0).getValue());
return;
}
List<ComponentLogEntryValue> componentValues = entry.getComponentValues();
for (int i = 0, componentValuesSize = componentValues.size(); i < componentValuesSize; i++) {
ComponentLogEntryValue v = componentValues.get(i);
results.put(entry.getName() + "_" + (i + 1), v.getValue());
}
});
return RSSFileResponse.builder().filename(file.getFilename()).result(results).build();
}
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
public DetailedRSSResponse getDetailedRSS(@PathVariable(DOSSIER_ID) String dossierId, @RequestParam(value = "fileId", required = false) String fileId) {
List<FileStatus> dossierFiles;
if (StringUtils.isBlank(fileId)) {
dossierFiles = statusController.getDossierStatus(dossierId);
} else {
dossierFiles = List.of(statusController.getFileStatus(dossierId, fileId));
}
List<DetailedRSSFileResponse> fileResponses = dossierFiles.stream()
.map(this::getDetailedRssResponse)
.toList();
return new DetailedRSSResponse(fileResponses);
}
private DetailedRSSFileResponse getDetailedRssResponse(FileStatus file) {
var componentLog = componentLogService.getComponentLog(file.getDossierId(), file.getId(), true);
Map<String, SCMComponent> results = new LinkedHashMap<>();
componentLog.getComponentLogEntries()
.forEach(entry -> {
if (entry.getComponentValues().size() <= 1) {
results.put(entry.getName(),
toSCMComponent(entry.getComponentValues()
.get(0)));
return;
}
List<ComponentLogEntryValue> componentValues = entry.getComponentValues();
for (int i = 0, componentValuesSize = componentValues.size(); i < componentValuesSize; i++) {
ComponentLogEntryValue v = componentValues.get(i);
results.put(entry.getName() + "_" + (i + 1), toSCMComponent(v));
}
});
return DetailedRSSFileResponse.builder().filename(file.getFilename()).result(results).build();
}
private SCMComponent toSCMComponent(ComponentLogEntryValue v) {
return SCMComponent.builder()
.value(v.getValue().equals(v.getOriginalValue()) ? null : v.getValue())
.originalValue(v.getOriginalValue())
.transformation(v.getValueDescription())
.scmAnnotations(v.getComponentLogEntityReferences()
.stream()
.map(this::toScmAnnotation)
.toList())
.build();
}
private ScmAnnotation toScmAnnotation(ComponentLogEntityReference er) {
return ScmAnnotation.builder().type(er.getType()).pages(Set.of(er.getPage())).ruleIdentifier(er.getEntityRuleId()).reason(formatType(er.getType())).build();
}
private static String formatType(String type) {
return type.substring(0, 1).toUpperCase(Locale.ENGLISH) + type.substring(1).toLowerCase(Locale.ENGLISH).replaceAll("_", " ");
}
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
public void addOverrides(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody ComponentsOverrides componentsOverrides) {
var componentLog = componentLogService.getComponentLog(dossierId, fileId);
var allComponents = componentLog.getComponentLogEntries();
componentOverrideService.addOverrides(dossierId, fileId, componentsOverrides);
componentsOverrides.getComponentOverrides()
.forEach((componentName, overrideValue) -> auditOverride(dossierId, fileId, componentName, overrideValue, allComponents));
}
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
public ComponentsOverrides getOverrides(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId) {
return componentOverrideService.getOverrides(dossierId, fileId);
}
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
public void revertOverrides(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody RevertOverrideRequest revertOverrideRequest) {
var componentLog = componentLogService.getComponentLog(dossierId, fileId);
var allComponents = componentLog.getComponentLogEntries();
componentOverrideService.revertOverrides(dossierId, fileId, revertOverrideRequest);
revertOverrideRequest.getComponents()
.forEach(componentNameToRevert -> auditOverrideRevert(dossierId, fileId, componentNameToRevert, allComponents));
}
private void auditOverride(String dossierId, String fileId, String componentName, String overrideValue, List<ComponentLogEntry> allComponentLogEntries) {
Optional<ComponentLogEntry> component = allComponentLogEntries.stream()
.filter(c -> c.getName().equals(componentName))
.findFirst();
String originalValue = getOriginalValue(component);
String value = getValue(component);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("The component is overwritten with value")
.details(Map.of(DOSSIER_ID,
dossierId,
FILE_ID,
fileId,
"ComponentName",
componentName,
"Action",
"MODIFY",
"OriginalValue",
originalValue,
"OldValue",
value,
"NewValue",
overrideValue))
.build());
}
private void auditOverrideRevert(String dossierId, String fileId, String componentNameToRevert, List<ComponentLogEntry> allComponentLogEntries) {
Optional<ComponentLogEntry> component = allComponentLogEntries.stream()
.filter(c -> c.getName().equals(componentNameToRevert))
.findFirst();
String originalValue = getOriginalValue(component);
String value = getValue(component);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("The component override for was reverted")
.details(Map.of(DOSSIER_ID,
dossierId,
FILE_ID,
fileId,
"ComponentName",
componentNameToRevert,
"Action",
"REVERT",
"OriginalValue",
originalValue,
"OldValue",
value,
"NewValue",
originalValue))
.build());
}
private String getValue(Optional<ComponentLogEntry> component) {
return component.map(ComponentLogEntry::getComponentValues)
.stream()
.map(a -> a.stream()
.map(ComponentLogEntryValue::getValue)
.collect(Collectors.joining(", ")))
.findFirst()
.orElse("");
}
private static String getOriginalValue(Optional<ComponentLogEntry> component) {
return component.map(ComponentLogEntry::getComponentValues)
.stream()
.map(a -> a.stream()
.map(ComponentLogEntryValue::getOriginalValue)
.collect(Collectors.joining(", ")))
.findFirst()
.orElse("");
}
}

View File

@ -1,148 +0,0 @@
package com.iqser.red.persistence.service.v1.external.api.impl.controller;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.GET_RSS;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.iqser.red.service.persistence.management.v1.processor.client.redactionreportservice.RssReportClient;
import com.iqser.red.service.persistence.management.v1.processor.service.ComponentOverrideService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
import com.iqser.red.service.persistence.service.v1.api.external.resource.RSSResource;
import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentsOverrides;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.RevertOverrideRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.rss.RSSFileResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.rss.RSSResponse;
import com.iqser.red.service.redaction.report.v1.api.model.rss.DetailedRSSResponse;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import lombok.RequiredArgsConstructor;
@Deprecated(forRemoval = true)
@RestController
@RequiredArgsConstructor
@ConditionalOnProperty(name = "application.rss.component-log.enabled", havingValue = "false")
public class RSSController implements RSSResource {
private final RssReportClient rssReportClient;
private final ComponentOverrideService componentOverrideService;
private final AuditPersistenceService auditPersistenceService;
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
public RSSResponse getRSS(@PathVariable(DOSSIER_ID) String dossierId, @RequestParam(value = "fileId", required = false) String fileId) {
return convert(rssReportClient.getRSS(dossierId, fileId));
}
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
private RSSResponse convert(com.iqser.red.service.redaction.report.v1.api.model.rss.RSSResponse rssResponse) {
return new RSSResponse(rssResponse.getFiles()
.stream()
.map(this::convert)
.collect(Collectors.toList()));
}
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
private RSSFileResponse convert(com.iqser.red.service.redaction.report.v1.api.model.rss.RSSFileResponse rssFileResponse) {
return new RSSFileResponse(rssFileResponse.getFilename(), rssFileResponse.getResult());
}
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
public DetailedRSSResponse getDetailedRSS(@PathVariable(DOSSIER_ID) String dossierId, @RequestParam(value = "fileId", required = false) String fileId) {
return rssReportClient.getDetailedRSS(dossierId, fileId);
}
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
public void addOverrides(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody ComponentsOverrides componentsOverrides) {
var rssReport = rssReportClient.getDetailedRSS(dossierId, fileId);
var components = rssReport.getFiles()
.get(0).getResult();
componentOverrideService.addOverrides(dossierId, fileId, componentsOverrides);
componentsOverrides.getComponentOverrides()
.forEach((key, value) -> auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("The component is overwritten with value")
.details(Map.of(DOSSIER_ID,
dossierId,
FILE_ID,
fileId,
"ComponentName",
key,
"Action",
"MODIFY",
"OriginalValue",
components.get(key).getOriginalValue(),
"OldValue",
components.get(key).getValue() != null ? components.get(key)
.getValue() : components.get(key).getOriginalValue(),
"NewValue",
value))
.build()));
}
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
public ComponentsOverrides getOverrides(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId) {
return componentOverrideService.getOverrides(dossierId, fileId);
}
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
public void revertOverrides(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody RevertOverrideRequest revertOverrideRequest) {
var rssReport = rssReportClient.getDetailedRSS(dossierId, fileId);
var components = rssReport.getFiles()
.get(0).getResult();
componentOverrideService.revertOverrides(dossierId, fileId, revertOverrideRequest);
revertOverrideRequest.getComponents()
.forEach(component -> auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("The component override for was reverted")
.details(Map.of(DOSSIER_ID,
dossierId,
FILE_ID,
fileId,
"ComponentName",
component,
"Action",
"REVERT",
"OriginalValue",
components.get(component).getOriginalValue(),
"OldValue",
components.get(component).getValue() != null ? components.get(component)
.getValue() : components.get(component).getOriginalValue(),
"NewValue",
components.get(component).getOriginalValue()))
.build()));
}
}

View File

@ -1,27 +1,24 @@
package com.iqser.red.persistence.service.v2.external.api.impl.controller;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.GET_RSS;
import static com.iqser.red.service.persistence.service.v2.api.external.resource.DossierResource.DOSSIER_ID_PARAM;
import static com.iqser.red.service.persistence.service.v2.api.external.resource.DossierTemplateResource.DOSSIER_TEMPLATE_ID_PARAM;
import static com.iqser.red.service.persistence.service.v2.api.external.resource.FileResource.FILE_ID_PARAM;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.iqser.red.persistence.service.v1.external.api.impl.controller.StatusController;
import com.iqser.red.persistence.service.v2.external.api.impl.mapper.ComponentMapper;
import com.iqser.red.service.persistence.management.v1.processor.service.ComponentLogService;
import com.iqser.red.service.persistence.management.v1.processor.service.FileStatusService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierTemplatePersistenceService;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntityReference;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntry;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntryValue;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.RevertOverrideRequest;
import com.iqser.red.service.persistence.service.v2.api.external.model.Component;
import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentValue;
import com.iqser.red.service.persistence.service.v2.api.external.model.EntityReference;
import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentOverrideList;
import com.iqser.red.service.persistence.service.v2.api.external.model.FileComponents;
import com.iqser.red.service.persistence.service.v2.api.external.model.FileComponentsList;
import com.iqser.red.service.persistence.service.v2.api.external.resource.ComponentResource;
@ -41,6 +38,7 @@ public class ComponentControllerV2 implements ComponentResource {
StatusController statusController;
FileStatusService fileStatusService;
DossierTemplatePersistenceService dossierTemplatePersistenceService;
ComponentMapper componentMapper = ComponentMapper.INSTANCE;
@Override
@ -52,65 +50,7 @@ public class ComponentControllerV2 implements ComponentResource {
dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId);
var componentLog = componentLogService.getComponentLog(dossierId, fileId, true);
Map<String, List<String>> basicComponent = new LinkedHashMap<>();
for (ComponentLogEntry componentLogEntry : componentLog.getComponentLogEntries()) {
basicComponent.put(componentLogEntry.getName(),
componentLogEntry.getComponentValues()
.stream()
.map(ComponentLogEntryValue::getValue)
.toList());
}
Map<String, Component> componentsDetails = new LinkedHashMap<>();
if (includeDetails) {
for (ComponentLogEntry entry : componentLog.getComponentLogEntries()) {
componentsDetails.put(entry.getName(), Component.builder().name(entry.getName()).componentValues(toComponentList(entry)).build());
}
}
return FileComponents.builder()
.dossierTemplateId(dossierTemplateId)
.dossierId(dossierId)
.filename(fileStatusService.getFileName(fileId))
.fileId(fileId)
.components(basicComponent)
.componentDetails(componentsDetails)
.build();
}
private List<ComponentValue> toComponentList(ComponentLogEntry componentLogEntry) {
return componentLogEntry.getComponentValues()
.stream()
.map(this::convert)
.toList();
}
private ComponentValue convert(ComponentLogEntryValue componentValue) {
return ComponentValue.builder()
.valueDescription(componentValue.getValueDescription())
.componentRuleId(componentValue.getComponentRuleId())
.entityReferences(componentValue.getComponentLogEntityReferences()
.stream()
.map(this::convertComponentEntityReference)
.toList())
.originalValue(componentValue.getOriginalValue())
.value(componentValue.getValue())
.build();
}
private EntityReference convertComponentEntityReference(ComponentLogEntityReference componentLogEntityReference) {
return EntityReference.builder()
.id(componentLogEntityReference.getId())
.entityRuleId(componentLogEntityReference.getEntityRuleId())
.type(componentLogEntityReference.getType())
.page(componentLogEntityReference.getPage())
.build();
return componentMapper.toFileComponents(componentLog, dossierTemplateId, dossierId, fileId, fileStatusService.getFileName(fileId), includeDetails);
}
@ -127,4 +67,44 @@ public class ComponentControllerV2 implements ComponentResource {
}
@Override
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
public void addOverride(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@PathVariable(DOSSIER_ID_PARAM) String dossierId,
@PathVariable(FILE_ID_PARAM) String fileId,
@RequestBody Component override) {
dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId);
componentLogService.addOverride(dossierId, fileId, componentMapper.toComponentLogEntry(override));
}
@Override
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
public ComponentOverrideList getOverrides(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@PathVariable(DOSSIER_ID_PARAM) String dossierId,
@PathVariable(FILE_ID_PARAM) String fileId) {
dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId);
var overrides = componentLogService.getOverrides(dossierId, fileId);
var componentOverrides = componentMapper.toComponents(overrides);
return ComponentOverrideList.builder().dossierTemplateId(dossierTemplateId).dossierId(dossierId).fileId(fileId).componentOverrides(componentOverrides).build();
}
@Override
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
public void revertOverrides(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@PathVariable(DOSSIER_ID_PARAM) String dossierId,
@PathVariable(FILE_ID_PARAM) String fileId,
@RequestBody RevertOverrideRequest revertOverrideRequest) {
dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId);
componentLogService.revertOverrides(dossierId, fileId, revertOverrideRequest);
}
}

View File

@ -0,0 +1,73 @@
package com.iqser.red.persistence.service.v2.external.api.impl.mapper;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLog;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntry;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntryValue;
import com.iqser.red.service.persistence.service.v2.api.external.model.Component;
import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentValue;
import com.iqser.red.service.persistence.service.v2.api.external.model.FileComponents;
@Mapper
public interface ComponentMapper {
ComponentMapper INSTANCE = Mappers.getMapper(ComponentMapper.class);
@Mapping(source = "componentLogEntityReferences", target = "entityReferences")
ComponentValue toComponentValue(ComponentLogEntryValue entry);
@Mapping(source = "entityReferences", target = "componentLogEntityReferences")
ComponentLogEntryValue toComponentLogEntry(ComponentValue value);
List<ComponentValue> toComponentValues(List<ComponentLogEntryValue> entries);
List<ComponentLogEntryValue> toComponentLogEntries(List<ComponentValue> values);
Component toComponent(ComponentLogEntry entry);
List<Component> toComponents(List<ComponentLogEntry> entries);
ComponentLogEntry toComponentLogEntry(Component component);
default FileComponents toFileComponents(ComponentLog componentLog, String dossierTemplateId, String dossierId, String fileId, String fileName, boolean includeDetails) {
Map<String, List<String>> basicComponent = new LinkedHashMap<>();
for (ComponentLogEntry componentLogEntry : componentLog.getComponentLogEntries()) {
basicComponent.put(componentLogEntry.getName(),
componentLogEntry.getComponentValues()
.stream()
.map(ComponentLogEntryValue::getValue)
.toList());
}
Map<String, Component> componentsDetails = new LinkedHashMap<>();
if (includeDetails) {
for (ComponentLogEntry entry : componentLog.getComponentLogEntries()) {
componentsDetails.put(entry.getName(), toComponent(entry));
}
}
return FileComponents.builder()
.dossierTemplateId(dossierTemplateId)
.dossierId(dossierId)
.filename(fileName)
.fileId(fileId)
.components(basicComponent)
.componentDetails(componentsDetails)
.build();
}
}

View File

@ -11,7 +11,8 @@ import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLog;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentsOverrides;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntry;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentOverrideList;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.RevertOverrideRequest;
import io.swagger.v3.oas.annotations.Operation;
@ -25,11 +26,13 @@ public interface ComponentLogResource {
String OVERRIDE_PATH = "/override";
String DOSSIER_TEMPLATE_ID = "dossierTemplateId";
String DOSSIER_TEMPLATE_ID_PATH_VARIABLE = "/{" + DOSSIER_TEMPLATE_ID + "}";
String DOSSIER_ID = "dossierId";
String DOSSIER_ID_PATH_VARIABLE = "/{" + DOSSIER_ID + "}";
String FILE_ID = "fileId";
String FILE_ID_PATH_VARIABLE = "/{" + FILE_ID + "}";
@ -46,7 +49,10 @@ public interface ComponentLogResource {
@PostMapping(value = COMPONENT_LOG_PATH + OVERRIDE_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Adds overrides for components", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "404", description = "Not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
void addOverrides(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody ComponentsOverrides componentsOverrides);
void addOverride(@RequestParam(name = "dossierTemplateId") String dossierTemplateId,
@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody ComponentLogEntry override);
@ResponseBody
@ -54,7 +60,9 @@ public interface ComponentLogResource {
@GetMapping(value = COMPONENT_LOG_PATH + OVERRIDE_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Gets overrides for components", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Not found")})
ComponentsOverrides getOverrides(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId);
ComponentOverrideList getOverrides(@RequestParam(name = "dossierTemplateId") String dossierTemplateId,
@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId);
@ResponseBody
@ -62,6 +70,9 @@ public interface ComponentLogResource {
@PostMapping(value = COMPONENT_LOG_PATH + OVERRIDE_PATH + "/revert" + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Reverts overrides for components", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "404", description = "Not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
void revertOverrides(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody RevertOverrideRequest revertOverrideRequest);
void revertOverrides(@RequestParam(name = "dossierTemplateId") String dossierTemplateId,
@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody RevertOverrideRequest revertOverrideRequest);
}

View File

@ -1,76 +0,0 @@
package com.iqser.red.service.persistence.service.v1.api.external.resource;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
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.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentsOverrides;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.RevertOverrideRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.rss.RSSResponse;
import com.iqser.red.service.redaction.report.v1.api.model.rss.DetailedRSSResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
@Deprecated(forRemoval = true)
@ResponseStatus(value = HttpStatus.OK)
public interface RSSResource {
String RSS_PATH = ExternalApi.BASE_PATH + "/rss";
String OVERRIDE_PATH = "/override";
String DOSSIER_ID = "dossierId";
String DOSSIER_ID_PATH_VARIABLE = "/{" + DOSSIER_ID + "}";
String FILE_ID = "fileId";
String FILE_ID_PATH_VARIABLE = "/{" + FILE_ID + "}";
@Deprecated(forRemoval = true)
@GetMapping(value = RSS_PATH + DOSSIER_ID_PATH_VARIABLE, produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
@Operation(summary = "Returns the RSS response for a dossier", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
RSSResponse getRSS(@PathVariable(DOSSIER_ID) String dossierId, @RequestParam(value = "fileId", required = false) String fileId);
@Deprecated(forRemoval = true)
@GetMapping(value = RSS_PATH + "/detailed" + DOSSIER_ID_PATH_VARIABLE, produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
@Operation(summary = "Returns the RSS response with more details for a dossier", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
DetailedRSSResponse getDetailedRSS(@PathVariable(DOSSIER_ID) String dossierId, @RequestParam(value = "fileId", required = false) String fileId);
@Deprecated(forRemoval = true)
@ResponseBody
@ResponseStatus(value = HttpStatus.OK)
@PostMapping(value = RSS_PATH + OVERRIDE_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Adds overrides for RSS components", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
void addOverrides(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody ComponentsOverrides componentsOverrides);
@Deprecated(forRemoval = true)
@ResponseBody
@ResponseStatus(value = HttpStatus.OK)
@GetMapping(value = RSS_PATH + OVERRIDE_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Gets overrides for RSS components", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
ComponentsOverrides getOverrides(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId);
@Deprecated(forRemoval = true)
@ResponseBody
@ResponseStatus(value = HttpStatus.OK)
@PostMapping(value = RSS_PATH + OVERRIDE_PATH + "/revert" + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Reverts overrides for RSS components", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
void revertOverrides(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody RevertOverrideRequest revertOverrideRequest);
}

View File

@ -19,5 +19,7 @@ public class Component {
private String name;
@JacksonXmlCData
private List<ComponentValue> componentValues;
@JacksonXmlCData
private boolean overridden;
}

View File

@ -0,0 +1,22 @@
package com.iqser.red.service.persistence.service.v2.api.external.model;
import java.util.ArrayList;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ComponentOverrideList {
String dossierTemplateId;
String dossierId;
String fileId;
List<Component> componentOverrides = new ArrayList<>();
}

View File

@ -18,6 +18,7 @@ public class ComponentValue {
@JacksonXmlCData
String value;
@JacksonXmlCData
@Deprecated
String originalValue;
@JacksonXmlCData
String valueDescription;

View File

@ -11,26 +11,22 @@ import static com.iqser.red.service.persistence.service.v2.api.external.resource
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.multipart.MultipartFile;
import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentMappingMetadataModel;
import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentMappingSummary;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.RevertOverrideRequest;
import com.iqser.red.service.persistence.service.v2.api.external.model.Component;
import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentOverrideList;
import com.iqser.red.service.persistence.service.v2.api.external.model.FileComponents;
import com.iqser.red.service.persistence.service.v2.api.external.model.FileComponentsList;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
@ -49,10 +45,16 @@ public interface ComponentResource {
String INCLUDE_DETAILS_DESCRIPTION = """
A toggle to decide whether to include detailed component information in the response:
- true: The component object's field componentDetails stores detailed information about the source of its respective value(s).
- false (default): The component object does not contain a field componentDetails.
""";
String OVERRIDES_PATH = "/overrides";
String REVERT_PATH = "/revert";
String COMPONENT_OVERRIDE_PARAM = "componentOverride";
String REVERT_OVERRIDE_PARAM = "revertOverride";
@GetMapping(value = FILE_PATH + FILE_ID_PATH_VARIABLE + COMPONENTS_PATH, produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
@ -72,5 +74,35 @@ public interface ComponentResource {
@Parameter(name = INCLUDE_DETAILS_PARAM, description = INCLUDE_DETAILS_DESCRIPTION) @RequestParam(name = INCLUDE_DETAILS_PARAM, defaultValue = "false", required = false) boolean includeDetails);
@ResponseBody
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@PostMapping(value = FILE_PATH + FILE_ID_PATH_VARIABLE + OVERRIDES_PATH, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Adds overrides for components", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
void addOverride(@Parameter(name = DOSSIER_TEMPLATE_ID_PARAM, description = "The identifier of the dossier template that is used for the dossier.", required = true) @PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@Parameter(name = DOSSIER_ID_PARAM, description = "The identifier of the dossier that contains the file.", required = true) @PathVariable(DOSSIER_ID_PARAM) String dossierId,
@Parameter(name = FILE_ID_PARAM, description = "The identifier of the file that the components are requested for.", required = true) @PathVariable(FILE_ID_PARAM) String fileId,
@Parameter(name = COMPONENT_OVERRIDE_PARAM, description = "The object to override the component.", required = true) @RequestBody Component componentOverrideModel);
@ResponseBody
@ResponseStatus(value = HttpStatus.OK)
@GetMapping(value = FILE_PATH + FILE_ID_PATH_VARIABLE + OVERRIDES_PATH, produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
@Operation(summary = "Gets overrides for components", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Not found")})
ComponentOverrideList getOverrides(@Parameter(name = DOSSIER_TEMPLATE_ID_PARAM, description = "The identifier of the dossier template that is used for the dossier.", required = true) @PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@Parameter(name = DOSSIER_ID_PARAM, description = "The identifier of the dossier that contains the file.", required = true) @PathVariable(DOSSIER_ID_PARAM) String dossierId,
@Parameter(name = FILE_ID_PARAM, description = "The identifier of the file that the components are requested for.", required = true) @PathVariable(FILE_ID_PARAM) String fileId);
@ResponseBody
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@PostMapping(value = FILE_PATH + FILE_ID_PATH_VARIABLE + OVERRIDES_PATH + REVERT_PATH, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Reverts overrides for components", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
void revertOverrides(@Parameter(name = DOSSIER_TEMPLATE_ID_PARAM, description = "The identifier of the dossier template that is used for the dossier.", required = true) @PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@Parameter(name = DOSSIER_ID_PARAM, description = "The identifier of the dossier that contains the file.", required = true) @PathVariable(DOSSIER_ID_PARAM) String dossierId,
@Parameter(name = FILE_ID_PARAM, description = "The identifier of the file that the components are requested for.", required = true) @PathVariable(FILE_ID_PARAM) String fileId,
@Parameter(name = REVERT_OVERRIDE_PARAM) @RequestBody RevertOverrideRequest revertOverrideRequest);
}

View File

@ -953,6 +953,101 @@ paths:
$ref: '#/components/responses/403'
"500":
$ref: '#/components/responses/500'
/api/dossier-templates/{dossierTemplateId}/dossiers/{dossierId}/files/{fileId}/overrides:
post:
operationId: addOverride
tags:
- 4. Components
summary: Add the component overrides associated with a specific file.
description: |
This endpoint allows the user to add one or more overrides for a specific component of a dossier file. The component override data is provided in the request body.
Use this route to add overrides to the specified component.
parameters:
- $ref: '#/components/parameters/dossierTemplateId'
- $ref: '#/components/parameters/dossierId'
- $ref: '#/components/parameters/fileId'
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Component'
required: true
responses:
"200":
description: |
Component was successfully overridden.
"400":
$ref: '#/components/responses/400'
"403":
$ref: '#/components/responses/403'
"404":
$ref: '#/components/responses/404-file'
"500":
$ref: '#/components/responses/500'
get:
operationId: getOverrides
tags:
- 4. Components
summary: Returns all overrides from components associated with a specific file.
description: |
This endpoint retrieves all the overrides for components within a dossier file.
The response includes a list of all component overrides associated with the specified file.
parameters:
- $ref: '#/components/parameters/dossierTemplateId'
- $ref: '#/components/parameters/dossierId'
- $ref: '#/components/parameters/fileId'
responses:
"200":
content:
application/json:
schema:
$ref: '#/components/schemas/ComponentOverrideList'
application/xml:
schema:
$ref: '#/components/schemas/ComponentOverrideList'
description: |
Successfully fetched component overrides for the file in the dossier.
"404":
$ref: '#/components/responses/404-dossier'
"400":
$ref: '#/components/responses/400'
"403":
$ref: '#/components/responses/403'
"500":
$ref: '#/components/responses/500'
/api/dossier-templates/{dossierTemplateId}/dossiers/{dossierId}/files/{fileId}/overrides/revert:
post:
operationId: revertOverrides
tags:
- 4. Components
summary: Reverts the component overrides associated with a specific file.
description: |
Reverts all overrides for the specified components associated with a specific file.
Use this route to revert all overrides specified by the component names.
parameters:
- $ref: '#/components/parameters/dossierTemplateId'
- $ref: '#/components/parameters/dossierId'
- $ref: '#/components/parameters/fileId'
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/RevertOverrideRequest'
required: true
responses:
"200":
description: |
Component overrides were successfully reverted.
"400":
$ref: '#/components/responses/400'
"403":
$ref: '#/components/responses/403'
"404":
$ref: '#/components/responses/404-file'
"500":
$ref: '#/components/responses/500'
/api/license/active/usage:
post:
operationId: getReport
@ -1462,6 +1557,94 @@ components:
entityRuleId: DEF.13.37
type: another_entity_type
page: 456
ComponentOverrideList:
type: object
description: A list of component overrides and their associated components.
properties:
dossierTemplateId:
format: uuid
type: string
description: Identifier for the template associated with the dossier.
dossierId:
format: uuid
type: string
description: Identifier for the dossier.
fileId:
type: string
description: Identifier for the file.
componentOverrides:
type: array
description: List of files with their component details.
items:
$ref: '#/components/schemas/Component'
example:
dossierTemplateId: 046b6c7f-0b8a-43b9-b35d-6489e6daee91
dossierId: 046b6c7f-0b8a-43b9-b35d-6489e6daee91
fileId: 1fdbd888b39059c8cf171df26f62f8a5
componentOverrideModels:
- name: the component name
componentValues:
- value: my overwritten component value
originalValue: the original value
componentRuleId: COM.0.0
valueDescription: My value description
entityReferences:
- id: bcd22239-cedf-442f-a5a1-1664cba94dc6
entityRuleId: ABC.0.0
type: entity_type
page: 123
- id: bcd22239-c3df-442f-a5a1-1664cba94dc6_2
entityRuleId: ABC.0.0
type: entity_type
page: 124
- id: b748b89a-5679-4254-9286-1dd652d9970b
entityRuleId: DEF.13.37
type: another_entity_type
page: 456
- value: yet another component value
originalValue: yet another component value
componentRuleId: COM.0.1
valueDescription: Another value description
entityReferences:
- id: 70496456-a016-4679-81b1-6c8856dded6e
entityRuleId: XYZ.0.0
type: yet_another_entity_type
page: 123
overridden: true
- name: other component name
componentValues:
- value: my overwritten component value
originalValue: the original value
componentRuleId: COM.0.0
valueDescription: My value description
entityReferences:
- id: bcd22239-cedf-442f-a5a1-1664cba94dc6
entityRuleId: ABC.0.0
type: entity_type
page: 123
- id: bcd22239-c3df-442f-a5a1-1664cba94dc6_2
entityRuleId: ABC.0.0
type: entity_type
page: 124
- id: b748b89a-5679-4254-9286-1dd652d9970b
entityRuleId: DEF.13.37
type: another_entity_type
page: 456
overridden: true
RevertOverrideRequest:
type: object
description: Set of component names to revert.
properties:
components:
type: array
items:
type: string
uniqueItems: true
example:
components:
- component1
- component2
- component3
FileComponents:
type: object
description: Represents file details along with its associated components and values.

View File

@ -1,10 +1,8 @@
package com.iqser.red.service.persistence.v1.internal.api.controller;
import com.iqser.red.commons.spring.ErrorMessage;
import com.iqser.red.service.persistence.management.v1.processor.exception.*;
import io.swagger.v3.oas.annotations.Hidden;
import lombok.extern.slf4j.Slf4j;
import java.sql.SQLException;
import java.time.OffsetDateTime;
import java.util.stream.Collectors;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
@ -14,9 +12,16 @@ import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.sql.SQLException;
import java.time.OffsetDateTime;
import java.util.stream.Collectors;
import com.iqser.red.commons.spring.ErrorMessage;
import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
import com.iqser.red.service.persistence.management.v1.processor.exception.ConflictException;
import com.iqser.red.service.persistence.management.v1.processor.exception.DossierNotFoundException;
import com.iqser.red.service.persistence.management.v1.processor.exception.InvalidRulesException;
import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException;
import com.iqser.red.service.persistence.management.v1.processor.exception.RulesTimeoutDetectedException;
import io.swagger.v3.oas.annotations.Hidden;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RestControllerAdvice

View File

@ -0,0 +1,21 @@
package com.iqser.red.service.persistence.management.v1.processor.model;
import java.util.ArrayList;
import java.util.List;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntryValue;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ComponentOverride {
String name;
List<ComponentLogEntryValue> componentOverrideValues = new ArrayList<>();
}

View File

@ -2,13 +2,19 @@ package com.iqser.red.service.persistence.management.v1.processor.service;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Map;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLog;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntry;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentsOverrides;
import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.RevertOverrideRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.service.ComponentLogMongoService;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
@ -19,7 +25,8 @@ import lombok.experimental.FieldDefaults;
public class ComponentLogService {
private final FileManagementStorageService fileManagementStorageService;
private final ComponentOverrideService componentOverrideService;
private final ComponentLogMongoService componentLogMongoService;
private final AuditPersistenceService auditPersistenceService;
// TODO: make this DB changeable!
private static final List<String> ORDER = List.of("Study_Title",
@ -65,30 +72,33 @@ public class ComponentLogService {
public ComponentLog getComponentLog(String dossierId, String fileId, boolean includeOverrides) {
ComponentLog componentLog = sortComponentLogEntriesByOrderList(fileManagementStorageService.getComponentLog(dossierId, fileId), ORDER);
ComponentLog componentLog = fileManagementStorageService.getComponentLog(dossierId, fileId);
if (!includeOverrides) {
componentLog = sortComponentLogEntriesByOrderList(componentLog, ORDER);
return componentLog;
}
ComponentsOverrides componentsOverrides = componentOverrideService.getOverrides(dossierId, fileId);
if (Objects.isNull(componentsOverrides.getComponentOverrides()) || componentsOverrides.getComponentOverrides().isEmpty()) {
return componentLog;
}
List<ComponentLogEntry> componentOverrides = getOverrides(dossierId, fileId);
List<ComponentLogEntry> overriddenComponentLogEntries = componentLog.getComponentLogEntries()
.stream()
.map(componentLogEntry -> applyOverride(componentLogEntry,
componentsOverrides.getComponentOverrides()
.get(componentLogEntry.getName())))
.toList();
replaceOverriddenComponentLogEntries(componentLog, componentOverrides);
componentLog.setComponentLogEntries(overriddenComponentLogEntries);
componentLog = sortComponentLogEntriesByOrderList(componentLog, ORDER);
return componentLog;
}
private void replaceOverriddenComponentLogEntries(ComponentLog componentLog, List<ComponentLogEntry> componentOverrides) {
componentLog.getComponentLogEntries()
.removeIf(entry -> componentOverrides.stream()
.anyMatch(override -> entry.getName().equals(override.getName())));
componentLog.getComponentLogEntries().addAll(componentOverrides);
}
private ComponentLog sortComponentLogEntriesByOrderList(ComponentLog componentLog, List<String> order) {
return new ComponentLog(componentLog.getAnalysisNumber(),
@ -107,14 +117,90 @@ public class ComponentLogService {
}
private ComponentLogEntry applyOverride(ComponentLogEntry componentLogEntry, String override) {
@Transactional
public void addOverride(String dossierId, String fileId, ComponentLogEntry componentOverride) {
if (Objects.isNull(override)) {
return componentLogEntry;
var optionalComponentLogEntry = componentLogMongoService.findComponentLogEntryById(dossierId, fileId, componentOverride.getName());
ComponentLogEntry componentToUpdate;
if (optionalComponentLogEntry.isPresent()) {
componentToUpdate = optionalComponentLogEntry.get();
updateComponentLogEntry(dossierId, fileId, componentOverride, componentToUpdate);
} else {
insertOverride(dossierId, fileId, componentOverride);
auditOverride(dossierId, fileId, componentOverride);
}
componentLogEntry.getComponentValues()
.forEach(componentValue -> componentValue.setValue(override));
return componentLogEntry;
}
private void updateComponentLogEntry(String dossierId, String fileId, ComponentLogEntry componentOverride, ComponentLogEntry componentToUpdate) {
componentToUpdate.setComponentValues(componentOverride.getComponentValues());
saveOverride(dossierId, fileId, componentToUpdate);
auditOverride(dossierId, fileId, componentToUpdate);
}
private void saveOverride(String dossierId, String fileId, ComponentLogEntry componentToUpdate) {
componentLogMongoService.saveComponentLogEntries(dossierId, fileId, List.of(componentToUpdate));
}
private void insertOverride(String dossierId, String fileId, ComponentLogEntry componentToAdd) {
componentLogMongoService.insertComponentLogEntries(dossierId, fileId, List.of(componentToAdd));
}
public List<ComponentLogEntry> getOverrides(String dossierId, String fileId) {
return componentLogMongoService.findOverrides(dossierId, fileId);
}
public void revertOverrides(String dossierId, String fileId, RevertOverrideRequest revertOverrideRequest) {
revertOverrideRequest.getComponents()
.forEach(componentName -> {
componentLogMongoService.deleteOverrides(dossierId, fileId, componentName);
auditOverrideRevert(dossierId, fileId, componentName);
});
}
private void auditOverride(String dossierId, String fileId, ComponentLogEntry entry) {
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("The component is overwritten with value")
.details(Map.of("dossierId",
dossierId,
"fileId",
fileId,
"ComponentName",
entry.getName(),
"Action",
"MODIFY",
"Values",
entry.getComponentValues()))
.build());
}
private void auditOverrideRevert(String dossierId, String fileId, String componentName) {
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("The component override for was reverted")
.details(Map.of("dossierId", dossierId, "fileId", fileId, "ComponentName", componentName, "Action", "REVERT"))
.build());
}
@ -148,4 +234,4 @@ public class ComponentLogService {
}
}
}

View File

@ -1,68 +0,0 @@
package com.iqser.red.service.persistence.management.v1.processor.service;
import java.io.ByteArrayInputStream;
import java.util.Collections;
import org.springframework.stereotype.Service;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentsOverrides;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.RevertOverrideRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileType;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
@Service
@RequiredArgsConstructor
public class ComponentOverrideService {
private final FileManagementStorageService fileManagementStorageService;
private final ObjectMapper objectMapper;
@SneakyThrows
public void addOverrides(String dossierId, String fileId, ComponentsOverrides componentsOverrides) {
if (!fileManagementStorageService.objectExists(dossierId, fileId, FileType.COMPONENTS)) {
fileManagementStorageService.storeObject(dossierId, fileId, FileType.COMPONENTS, new ByteArrayInputStream(objectMapper.writeValueAsBytes(componentsOverrides)));
return;
}
var existingComponentsBytes = fileManagementStorageService.getStoredObjectBytes(dossierId, fileId, FileType.COMPONENTS);
var existingComponents = objectMapper.readValue(existingComponentsBytes, ComponentsOverrides.class);
existingComponents.getComponentOverrides().putAll(componentsOverrides.getComponentOverrides());
fileManagementStorageService.storeObject(dossierId, fileId, FileType.COMPONENTS, new ByteArrayInputStream(objectMapper.writeValueAsBytes(existingComponents)));
}
@SneakyThrows
public ComponentsOverrides getOverrides(String dossierId, String fileId) {
var exists = fileManagementStorageService.objectExists(dossierId, fileId, FileType.COMPONENTS);
if (!exists) {
return ComponentsOverrides.builder().componentOverrides(Collections.emptyMap()).build();
}
var existingComponentsBytes = fileManagementStorageService.getStoredObjectBytes(dossierId, fileId, FileType.COMPONENTS);
return objectMapper.readValue(existingComponentsBytes, ComponentsOverrides.class);
}
@SneakyThrows
public void revertOverrides(String dossierId, String fileId, RevertOverrideRequest revertOverrideRequest) {
if (!fileManagementStorageService.objectExists(dossierId, fileId, FileType.COMPONENTS)) {
return;
}
var existingComponentsBytes = fileManagementStorageService.getStoredObjectBytes(dossierId, fileId, FileType.COMPONENTS);
var existingComponents = objectMapper.readValue(existingComponentsBytes, ComponentsOverrides.class);
revertOverrideRequest.getComponents()
.forEach(c -> existingComponents.getComponentOverrides().remove(c));
fileManagementStorageService.storeObject(dossierId, fileId, FileType.COMPONENTS, new ByteArrayInputStream(objectMapper.writeValueAsBytes(existingComponents)));
}
}

View File

@ -5,4 +5,5 @@
<include file="/mongo/changelog/tenant/1-initial-database.changelog.xml"/>
<include file="/mongo/changelog/tenant/2-create-indices-for-entries.xml"/>
<include file="/mongo/changelog/tenant/3-add-page-paragraph-idx.xml"/>
<include file="/mongo/changelog/tenant/4-create-component-entities.xml"/>
</databaseChangeLog>

View File

@ -0,0 +1,106 @@
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.20.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd">
<changeSet id="createComponentCollection" author="ali">
<ext:createCollection collectionName="component-logs">
<ext:options>
{
"collMod": "component-logs",
"validator": {
"$jsonSchema": {
"bsonType": "object",
"required": ["id", "dossierId", "fileId", "analysisNumber", "componentRulesVersion", "components"],
"properties": {
"id": {
"bsonType": "string",
"description": "must be a string and is required"
},
"dossierId": {
"bsonType": "string",
"description": "must be a string and is required"
},
"fileId": {
"bsonType": "string",
"description": "must be a string and is required"
},
"analysisNumber": {
"bsonType": "int",
"description": "must be an integer and is required"
},
"componentRulesVersion": {
"bsonType": "long",
"description": "must be a long and is required"
},
"components": {
"bsonType": "array",
"items": {
"bsonType": "objectId",
"description": "must be an array of objectIds"
},
"description": "must be an array and is required"
}
}
}
},
"validationLevel": "strict",
"validationAction": "error"
}
</ext:options>
</ext:createCollection>
<ext:createCollection collectionName="component-log-entries">
<ext:options>
{
"collMod": "component-log-entries",
"validator": {
"$jsonSchema": {
"bsonType": "object",
"required": ["id", "componentLogId", "name", "overrideValues", "values", "overridden"],
"properties": {
"id": {
"bsonType": "string",
"description": "must be a string and is required"
},
"componentLogId": {
"bsonType": "string",
"description": "must be a string and is required"
},
"name": {
"bsonType": "string",
"description": "must be a string and is required"
},
"overrideValues": {
"bsonType": "array",
"items": {
"bsonType": "object"
},
"description": "must be an array of objects and is required"
},
"values": {
"bsonType": "array",
"items": {
"bsonType": "object"
},
"description": "must be an array of objects and is required"
},
"overridden": {
"bsonType": "bool",
"description": "must be a boolean and is required"
}
}
}
},
"validationLevel": "strict",
"validationAction": "warn"
}
</ext:options>
</ext:createCollection>
</changeSet>
</databaseChangeLog>

View File

@ -0,0 +1,10 @@
package com.iqser.red.service.peristence.v1.server.integration.client;
import org.springframework.cloud.openfeign.FeignClient;
import com.iqser.red.service.persistence.service.v2.api.external.resource.ComponentResource;
@FeignClient(name = "ComponentResource", url = "http://localhost:28081")
public interface ComponentClient extends ComponentResource {
}

View File

@ -0,0 +1,10 @@
package com.iqser.red.service.peristence.v1.server.integration.client;
import org.springframework.cloud.openfeign.FeignClient;
import com.iqser.red.service.persistence.service.v1.api.external.resource.ComponentLogResource;
@FeignClient(name = "ComponentLogResource", url = "http://localhost:28081")
public interface ComponentLogClient extends ComponentLogResource {
}

View File

@ -0,0 +1,214 @@
package com.iqser.red.service.peristence.v1.server.integration.tests;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.IOException;
import java.util.List;
import java.util.Set;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.iqser.red.service.peristence.v1.server.integration.client.ComponentClient;
import com.iqser.red.service.peristence.v1.server.integration.client.ComponentLogClient;
import com.iqser.red.service.peristence.v1.server.integration.client.DossierTemplateClient;
import com.iqser.red.service.peristence.v1.server.integration.service.DossierTesterAndProvider;
import com.iqser.red.service.peristence.v1.server.integration.service.FileTesterAndProvider;
import com.iqser.red.service.peristence.v1.server.integration.utils.AbstractPersistenceServerServiceTest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntityReference;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntry;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntryValue;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.RevertOverrideRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileType;
import com.iqser.red.service.persistence.service.v2.api.external.model.Component;
import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentValue;
import com.iqser.red.service.persistence.service.v2.api.external.model.EntityReference;
public class ComponentOverrideTest extends AbstractPersistenceServerServiceTest {
@Autowired
private ObjectMapper objectMapper;
@Autowired
private DossierTesterAndProvider dossierTesterAndProvider;
@Autowired
private FileTesterAndProvider fileTesterAndProvider;
@Autowired
private ComponentClient componentClient;
@Autowired
private ComponentLogClient componentLogClient;
@Autowired
private DossierTemplateClient dossierTemplateClient;
@Test
public void testOverrides() throws IOException {
var dossier = dossierTesterAndProvider.provideTestDossier();
var dossierTemplate = dossierTemplateClient.getDossierTemplate(dossier.getDossierTemplateId());
var file = fileTesterAndProvider.testAndProvideFile(dossier, "filename");
System.out.println("DOSSIER TEMPLATE ID: " + dossierTemplate.getId());
System.out.println("DOSSIER ID: " + dossier.getId());
System.out.println("FILE ID: " + file.getId());
Component componentOverrideModel = Component.builder()
.name("Study_Title")
.componentValues(List.of(ComponentValue.builder()
.value("AAAA Strange Chemical Name And the rest of a title With a dash and some more text")
.originalValue("Strange Chemical Name And the rest of a title With a dash and some more text")
.valueDescription("First found value of type title or else ''")
.componentRuleId("StudyTitle.0.0")
.entityReferences(List.of(EntityReference.builder()
.id("cf7f0d0c4c07918ce7d67b204f5fdb7d")
.type("title")
.entityRuleId("DOC.6.1")
.page(1)
.build()))
.build()))
.build();
var componentLogJson = new ClassPathResource("files/componentlog/exampleComponentLog.json");
fileManagementStorageService.storeObject(dossier.getId(), file.getId(), FileType.COMPONENT_LOG, componentLogJson.getInputStream());
componentClient.addOverride(dossierTemplate.getId(), dossier.getId(), file.getId(), componentOverrideModel);
var overrides = componentClient.getOverrides(dossierTemplate.getId(), dossier.getId(), file.getId());
assertEquals(dossierTemplate.getId(), overrides.getDossierTemplateId());
assertEquals(dossier.getId(), overrides.getDossierId());
assertEquals(file.getId(), overrides.getFileId());
assertFalse(overrides.getComponentOverrides().isEmpty());
assertTrue(overrides.getComponentOverrides()
.get(0).isOverridden());
// override same entry a second time
Component componentOverrideModel2 = Component.builder()
.name("Study_Title")
.componentValues(List.of(ComponentValue.builder()
.value("BBBB Strange Chemical Name And the rest of a title With a dash and some more text")
.originalValue("Strange Chemical Name And the rest of a title With a dash and some more text")
.valueDescription("First found value of type title or else ''")
.componentRuleId("StudyTitle.0.0")
.entityReferences(List.of(EntityReference.builder()
.id("cf7f0d0c4c07918ce7d67b204f5fdb7d")
.type("title")
.entityRuleId("DOC.6.1")
.page(1)
.build()))
.build()))
.build();
componentClient.addOverride(dossierTemplate.getId(), dossier.getId(), file.getId(), componentOverrideModel2);
overrides = componentClient.getOverrides(dossierTemplate.getId(), dossier.getId(), file.getId());
assertEquals(dossierTemplate.getId(), overrides.getDossierTemplateId());
assertEquals(dossier.getId(), overrides.getDossierId());
assertEquals(file.getId(), overrides.getFileId());
assertFalse(overrides.getComponentOverrides().isEmpty());
assertTrue(overrides.getComponentOverrides()
.get(0).isOverridden());
// add and revert override
Component componentOverrideModel3 = Component.builder()
.name("Report_Number")
.componentValues(List.of(ComponentValue.builder()
.value("WOHOO 11/111-111A")
.originalValue("11/111-111A")
.valueDescription("First found value of type report_number or else ''")
.componentRuleId("ReportNumber.0.0")
.entityReferences(List.of(EntityReference.builder()
.id("e2a93bcc72e9740bbfc19bd9cd982e01")
.type("report_number")
.entityRuleId("DOC.2.0")
.page(1)
.build()))
.build()))
.build();
componentClient.addOverride(dossierTemplate.getId(), dossier.getId(), file.getId(), componentOverrideModel3);
overrides = componentClient.getOverrides(dossierTemplate.getId(), dossier.getId(), file.getId());
assertEquals(dossierTemplate.getId(), overrides.getDossierTemplateId());
assertEquals(dossier.getId(), overrides.getDossierId());
assertEquals(file.getId(), overrides.getFileId());
assertFalse(overrides.getComponentOverrides().isEmpty());
assertTrue(overrides.getComponentOverrides()
.stream()
.filter(component -> component.getName().equals("Report_Number"))
.findAny()
.get().isOverridden());
RevertOverrideRequest revertOverrideRequest = RevertOverrideRequest.builder().components(Set.of("Report_Number")).build();
componentClient.revertOverrides(dossierTemplate.getId(), dossier.getId(), file.getId(), revertOverrideRequest);
overrides = componentClient.getOverrides(dossierTemplate.getId(), dossier.getId(), file.getId());
assertEquals(dossierTemplate.getId(), overrides.getDossierTemplateId());
assertEquals(dossier.getId(), overrides.getDossierId());
assertEquals(file.getId(), overrides.getFileId());
assertFalse(overrides.getComponentOverrides().isEmpty());
assertFalse(overrides.getComponentOverrides()
.stream()
.anyMatch(component -> component.getName().equals("Report_Number")));
// testing old endpoints
var overridesFromOldEndpoint = componentLogClient.getComponentLog(dossier.getId(), file.getId(), true);
assertTrue(overridesFromOldEndpoint.getComponentLogEntries()
.stream()
.filter(component -> component.getName().equals("Study_Title"))
.findAny()
.get().isOverridden());
revertOverrideRequest = RevertOverrideRequest.builder().components(Set.of("Study_Title")).build();
componentLogClient.revertOverrides(dossierTemplate.getId(), dossier.getId(), file.getId(), revertOverrideRequest);
overridesFromOldEndpoint = componentLogClient.getComponentLog(dossier.getId(), file.getId(), true);
assertFalse(overridesFromOldEndpoint.getComponentLogEntries()
.stream()
.filter(component -> component.getName().equals("Study_Title"))
.findAny()
.get().isOverridden());
ComponentLogEntry componentOverrideModel4 = ComponentLogEntry.builder()
.name("Study_Title")
.componentValues(List.of(ComponentLogEntryValue.builder()
.value("BBBB Strange Chemical Name And the rest of a title With a dash and some more text")
.valueDescription("First found value of type title or else ''")
.componentRuleId("StudyTitle.0.0")
.componentLogEntityReferences(List.of(ComponentLogEntityReference.builder()
.id("cf7f0d0c4c07918ce7d67b204f5fdb7d")
.type("title")
.entityRuleId("DOC.6.1")
.page(1)
.build()))
.build()))
.build();
componentLogClient.addOverride(dossierTemplate.getId(), dossier.getId(), file.getId(), componentOverrideModel4);
overridesFromOldEndpoint = componentLogClient.getComponentLog(dossier.getId(), file.getId(), true);
assertTrue(overridesFromOldEndpoint.getComponentLogEntries()
.stream()
.filter(component -> component.getName().equals("Study_Title"))
.findAny()
.get().isOverridden());
}
}

View File

@ -12,14 +12,10 @@ import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.CommentRepository;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.annotationentity.ResizeRedactionRepository;
import org.assertj.core.util.Lists;
import org.bson.BsonArray;
import org.bson.BsonDocument;
import org.bson.BsonString;
import org.checkerframework.checker.units.qual.A;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.ExtendWith;
@ -54,7 +50,6 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.support.TestPropertySourceUtils;
@ -76,6 +71,7 @@ import com.iqser.red.service.persistence.management.v1.processor.service.EntityL
import com.iqser.red.service.persistence.management.v1.processor.service.FileManagementStorageService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.ApplicationConfigRepository;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.AuditRepository;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.CommentRepository;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.DigitalSignatureRepository;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.DossierAttributeConfigRepository;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.DossierAttributeRepository;
@ -101,14 +97,18 @@ import com.iqser.red.service.persistence.management.v1.processor.service.persist
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.annotationentity.ManualRedactionRepository;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.annotationentity.RecategorizationRepository;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.annotationentity.RemoveRedactionRepository;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.annotationentity.ResizeRedactionRepository;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.dictionaryentry.EntryRepository;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.dictionaryentry.FalsePositiveEntryRepository;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.dictionaryentry.FalseRecommendationEntryRepository;
import com.iqser.red.service.persistence.management.v1.processor.service.users.UserService;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntityLog;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.configuration.ApplicationConfig;
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.repository.ComponentDocumentRepository;
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.repository.ComponentLogDocumentRepository;
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.repository.EntityLogDocumentRepository;
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.repository.EntityLogEntryDocumentRepository;
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.service.ComponentLogMongoService;
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.service.EntityLogMongoService;
import com.iqser.red.service.redaction.v1.model.DroolsValidation;
import com.iqser.red.storage.commons.service.StorageService;
@ -168,6 +168,12 @@ public abstract class AbstractPersistenceServerServiceTest {
@Autowired
protected EntityLogEntryDocumentRepository entityLogEntryDocumentRepository;
@Autowired
protected ComponentLogMongoService componentLogMongoService;
@Autowired
protected ComponentLogDocumentRepository componentLogDocumentRepository;
@Autowired
protected ComponentDocumentRepository componentDocumentRepository;
@Autowired
protected FileManagementStorageService fileManagementStorageService;
@Autowired
protected DossierTemplateRepository dossierTemplateRepository;
@ -526,6 +532,8 @@ public abstract class AbstractPersistenceServerServiceTest {
applicationConfigRepository.deleteAll();
entityLogEntryDocumentRepository.deleteAll();
entityLogDocumentRepository.deleteAll();
componentLogDocumentRepository.deleteAll();
componentDocumentRepository.deleteAll();
});
}

View File

@ -0,0 +1,655 @@
{
"analysisNumber": 1,
"componentRulesVersion": 1,
"componentLogEntries": [
{
"name": "Study_Title",
"componentValues": [
{
"value": "Strange Chemical Name And the rest of a title With a dash and some more text",
"originalValue": "Strange Chemical Name And the rest of a title With a dash and some more text",
"valueDescription": "First found value of type title or else ''",
"componentRuleId": "StudyTitle.0.0",
"componentLogEntityReferences": [
{
"id": "cf7f0d0c4c07918ce7d67b204f5fdb7d",
"type": "title",
"entityRuleId": "DOC.6.1",
"page": 1
},
{
"id": "cf7f0d0c4c07918ce7d67b204f5fdb7d",
"type": "title",
"entityRuleId": "DOC.6.1",
"page": 1
}
]
}
],
"overridden": false
},
{
"name": "Performing_Laboratory",
"componentValues": [
{
"value": "Test Labor Deutschland AG, Switzerland",
"originalValue": "Test Labor Deutschland AG, Switzerland",
"valueDescription": "Laboratory name and country found!",
"componentRuleId": "PerformingLaboratory.1.0",
"componentLogEntityReferences": [
{
"id": "cdfa1386cc0b1c665c7dfb1b8bd2a134",
"type": "laboratory_name",
"entityRuleId": "DOC.7.0",
"page": 1
},
{
"id": "281e6f46a8b400cfb66e02ed51cd6a7a",
"type": "laboratory_country",
"entityRuleId": "DOC.7.2",
"page": 1
}
]
},
{
"value": "Test Labor Deutschland AG, Switzerland",
"originalValue": "Test Labor Deutschland AG, Switzerland",
"valueDescription": "Laboratory name and country found!",
"componentRuleId": "PerformingLaboratory.1.0",
"componentLogEntityReferences": [
{
"id": "cdfa1386cc0b1c665c7dfb1b8bd2a134",
"type": "laboratory_name",
"entityRuleId": "DOC.7.0",
"page": 1
},
{
"id": "281e6f46a8b400cfb66e02ed51cd6a7a",
"type": "laboratory_country",
"entityRuleId": "DOC.7.2",
"page": 1
}
]
},
{
"value": "Test Labor Deutschland AG, Switzerland",
"originalValue": "Test Labor Deutschland AG, Switzerland",
"valueDescription": "Laboratory name and country found!",
"componentRuleId": "PerformingLaboratory.1.0",
"componentLogEntityReferences": [
{
"id": "cdfa1386cc0b1c665c7dfb1b8bd2a134",
"type": "laboratory_name",
"entityRuleId": "DOC.7.0",
"page": 1
},
{
"id": "281e6f46a8b400cfb66e02ed51cd6a7a",
"type": "laboratory_country",
"entityRuleId": "DOC.7.2",
"page": 1
}
]
},
{
"value": "Test Labor Deutschland AG, Switzerland",
"originalValue": "Test Labor Deutschland AG, Switzerland",
"valueDescription": "Laboratory name and country found!",
"componentRuleId": "PerformingLaboratory.1.0",
"componentLogEntityReferences": [
{
"id": "cdfa1386cc0b1c665c7dfb1b8bd2a134",
"type": "laboratory_name",
"entityRuleId": "DOC.7.0",
"page": 1
},
{
"id": "281e6f46a8b400cfb66e02ed51cd6a7a",
"type": "laboratory_country",
"entityRuleId": "DOC.7.2",
"page": 1
}
]
}
],
"overridden": false
},
{
"name": "Report_Number",
"componentValues": [
{
"value": "11/111-111A",
"originalValue": "11/111-111A",
"valueDescription": "First found value of type report_number or else ''",
"componentRuleId": "ReportNumber.0.0",
"componentLogEntityReferences": [
{
"id": "e2a93bcc72e9740bbfc19bd9cd982e01",
"type": "report_number",
"entityRuleId": "DOC.2.0",
"page": 1
},
{
"id": "e2a93bcc72e9740bbfc19bd9cd982e01",
"type": "report_number",
"entityRuleId": "DOC.2.0",
"page": 1
}
]
}
],
"overridden": false
},
{
"name": "GLP_Study",
"componentValues": [
{
"value": "Yes",
"originalValue": "Yes",
"valueDescription": "Yes if present, No if not",
"componentRuleId": "GLPStudy.0.0",
"componentLogEntityReferences": [
{
"id": "f898e5d91723480a4595d70371066a7f",
"type": "glp_study",
"entityRuleId": "DOC.8.0",
"page": 2
},
{
"id": "f898e5d91723480a4595d70371066a7f",
"type": "glp_study",
"entityRuleId": "DOC.8.0",
"page": 2
},
{
"id": "bb6817a9356ce9569d314a321aa44ad1",
"type": "glp_study",
"entityRuleId": "DOC.8.0",
"page": 11
},
{
"id": "bb6817a9356ce9569d314a321aa44ad1",
"type": "glp_study",
"entityRuleId": "DOC.8.0",
"page": 11
}
]
}
],
"overridden": false
},
{
"name": "Test_Guidelines_1",
"componentValues": [
{
"value": "Nº 402: Acute Dermal Toxicity (09/10/2017)",
"originalValue": "Nº 402: Acute Dermal Toxicity (09/10/2017)",
"valueDescription": "OECD Number and guideline year mapped!",
"componentRuleId": "TestGuideline.0.0",
"componentLogEntityReferences": [
{
"id": "fa103d7d55a67aff195a42d46cf5b1b1",
"type": "oecd_guideline_number",
"entityRuleId": "DOC.1.0",
"page": 1
},
{
"id": "a6a07692a60d3b8ef8d1261f23ab20c7",
"type": "oecd_guideline_year",
"entityRuleId": "DOC.1.0",
"page": 1
}
]
}
],
"overridden": false
},
{
"name": "Test_Guidelines_2",
"componentValues": [
{
"value": "EPA 870.1200 (1998), EPA 870.1200 (1998), EC 440/2008, B.3 (2008), EC 440/2008, B.3 (2008)",
"originalValue": "EPA 870.1200 (1998), EPA 870.1200 (1998), EC 440/2008, B.3 (2008), EC 440/2008, B.3 (2008)",
"valueDescription": "Joining all values of type epa_guideline, ec_guideline with ', '",
"componentRuleId": "TestGuideline.2.0",
"componentLogEntityReferences": [
{
"id": "80890bae7f2ea65c43f2e82ab847c424",
"type": "epa_guideline",
"entityRuleId": "DOC.1.0",
"page": 1
},
{
"id": "80890bae7f2ea65c43f2e82ab847c424",
"type": "epa_guideline",
"entityRuleId": "DOC.1.0",
"page": 1
},
{
"id": "7a23ea595ed928d777d00f1afa61f552",
"type": "ec_guideline",
"entityRuleId": "DOC.1.0",
"page": 1
},
{
"id": "7a23ea595ed928d777d00f1afa61f552",
"type": "ec_guideline",
"entityRuleId": "DOC.1.0",
"page": 1
}
]
}
],
"overridden": false
},
{
"name": "Experimental_Starting_Date",
"componentValues": [
{
"value": "14/05/2010, 14/05/2010",
"originalValue": "14/05/2010, 14/05/2010",
"valueDescription": "Convert values of type 'experimental_start_date' to dd/MM/yyyy joined with ', '",
"componentRuleId": "StartDate.0.0",
"componentLogEntityReferences": [
{
"id": "9819e3a491c9841ba76a526b215c2adf",
"type": "experimental_start_date",
"entityRuleId": "DOC.3.0",
"page": 3
},
{
"id": "9819e3a491c9841ba76a526b215c2adf",
"type": "experimental_start_date",
"entityRuleId": "DOC.3.0",
"page": 3
}
]
}
],
"overridden": false
},
{
"name": "Experimental_Completion_Date",
"componentValues": [
{
"value": "21/05/2010, 21/05/2010",
"originalValue": "21/05/2010, 21/05/2010",
"valueDescription": "Convert values of type 'experimental_end_date' to dd/MM/yyyy joined with ', '",
"componentRuleId": "CompletionDate.0.0",
"componentLogEntityReferences": [
{
"id": "9a2a7922adf21476bd8718f154fc1fb0",
"type": "experimental_end_date",
"entityRuleId": "DOC.4.0",
"page": 3
},
{
"id": "9a2a7922adf21476bd8718f154fc1fb0",
"type": "experimental_end_date",
"entityRuleId": "DOC.4.0",
"page": 3
}
]
}
],
"overridden": false
},
{
"name": "Certificate_of_Analysis_Batch_Identification",
"componentValues": [
{
"value": "AAA111-111-111",
"originalValue": "AAA111-111-111",
"valueDescription": "Joining all unique values of type batch_number with ', '",
"componentRuleId": "AnalysisCertificate.0.0",
"componentLogEntityReferences": [
{
"id": "2910c0d523ad15f9619e3c6b46200533",
"type": "batch_number",
"entityRuleId": "DOC.9.1",
"page": 6
},
{
"id": "2910c0d523ad15f9619e3c6b46200533",
"type": "batch_number",
"entityRuleId": "DOC.9.1",
"page": 6
},
{
"id": "b028246b799b7e32c0aef31e413c187f",
"type": "batch_number",
"entityRuleId": "DOC.9.1",
"page": 10
},
{
"id": "b028246b799b7e32c0aef31e413c187f",
"type": "batch_number",
"entityRuleId": "DOC.9.1",
"page": 10
}
]
}
],
"overridden": false
},
{
"name": "Species",
"componentValues": [
{
"value": "rats",
"originalValue": "rats",
"valueDescription": "First found value of type species or else ''",
"componentRuleId": "Species.0.0",
"componentLogEntityReferences": [
{
"id": "5f0b08c0e3d98545cd7647aac82b6bf2",
"type": "species",
"entityRuleId": "DOC.5.2",
"page": 7
},
{
"id": "5f0b08c0e3d98545cd7647aac82b6bf2",
"type": "species",
"entityRuleId": "DOC.5.2",
"page": 7
},
{
"id": "18ab2f51b57c2f91cea9350621e7152f",
"type": "species",
"entityRuleId": "DOC.5.2",
"page": 7
},
{
"id": "18ab2f51b57c2f91cea9350621e7152f",
"type": "species",
"entityRuleId": "DOC.5.2",
"page": 7
},
{
"id": "5557fd15f4c4e3e519cce0f47f64d3e1",
"type": "species",
"entityRuleId": "DOC.5.2",
"page": 7
},
{
"id": "5557fd15f4c4e3e519cce0f47f64d3e1",
"type": "species",
"entityRuleId": "DOC.5.2",
"page": 7
},
{
"id": "665743f4475ff7c82e642b2901376b28",
"type": "species",
"entityRuleId": "DOC.5.2",
"page": 7
},
{
"id": "665743f4475ff7c82e642b2901376b28",
"type": "species",
"entityRuleId": "DOC.5.2",
"page": 7
}
]
}
],
"overridden": false
},
{
"name": "Strain",
"componentValues": [
{
"value": "Crl:WI Wistar",
"originalValue": "Crl:WI Wistar",
"valueDescription": "First found value of type strain or else ''",
"componentRuleId": "Strain.0.0",
"componentLogEntityReferences": [
{
"id": "a77b13568f8d6885faabf90119dd9b40",
"type": "strain",
"entityRuleId": "DOC.5.3",
"page": 7
},
{
"id": "a77b13568f8d6885faabf90119dd9b40",
"type": "strain",
"entityRuleId": "DOC.5.3",
"page": 7
},
{
"id": "818d354d9dc4158e4c05e96de099f39c",
"type": "strain",
"entityRuleId": "DOC.5.3",
"page": 7
},
{
"id": "818d354d9dc4158e4c05e96de099f39c",
"type": "strain",
"entityRuleId": "DOC.5.3",
"page": 7
}
]
}
],
"overridden": false
},
{
"name": "Doses_mg_per_kg_bw",
"componentValues": [
{
"value": "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.",
"originalValue": "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.",
"valueDescription": "Joining all values of type doses_(mg_kg_bw) with ' '",
"componentRuleId": "Necropsy.1.0",
"componentLogEntityReferences": [
{
"id": "92b67bcf7b5f7f7bdfcb6d0991e6b4c2",
"type": "doses_(mg_kg_bw)",
"entityRuleId": "DOC.35.0",
"page": 4
},
{
"id": "92b67bcf7b5f7f7bdfcb6d0991e6b4c2",
"type": "doses_(mg_kg_bw)",
"entityRuleId": "DOC.35.0",
"page": 4
},
{
"id": "67e8421d095ed4c003ca2c21a120c00b",
"type": "doses_(mg_kg_bw)",
"entityRuleId": "DOC.35.0",
"page": 4
},
{
"id": "67e8421d095ed4c003ca2c21a120c00b",
"type": "doses_(mg_kg_bw)",
"entityRuleId": "DOC.35.0",
"page": 4
}
]
}
],
"overridden": false
},
{
"name": "Mortality_Statement",
"componentValues": [
{
"value": "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.",
"originalValue": "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.",
"valueDescription": "Joining all values of type mortality_statement with ' '",
"componentRuleId": "MortalityStatement.0.0",
"componentLogEntityReferences": [
{
"id": "08b4d86a822d87d6f4e7b1efdd364181",
"type": "mortality_statement",
"entityRuleId": "DOC.32.0",
"page": 8
},
{
"id": "08b4d86a822d87d6f4e7b1efdd364181",
"type": "mortality_statement",
"entityRuleId": "DOC.32.0",
"page": 8
},
{
"id": "cc1c76d7e9bd09483b1ffa91a0749c2c",
"type": "mortality_statement",
"entityRuleId": "DOC.32.0",
"page": 8
},
{
"id": "cc1c76d7e9bd09483b1ffa91a0749c2c",
"type": "mortality_statement",
"entityRuleId": "DOC.32.0",
"page": 8
}
]
}
],
"overridden": false
},
{
"name": "Weight_Behavior_Changes",
"componentValues": [
{
"value": "",
"originalValue": "",
"valueDescription": "Joining all values of type with '\n'",
"componentRuleId": "WeightBehavior.0.0",
"componentLogEntityReferences": []
}
],
"overridden": false
},
{
"name": "Necropsy_Findings",
"componentValues": [
{
"value": "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.",
"originalValue": "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.",
"valueDescription": "Joining all values of type necropsy_findings with ' '",
"componentRuleId": "Necropsy.0.0",
"componentLogEntityReferences": [
{
"id": "069397e80191213e72c792f177a22185",
"type": "necropsy_findings",
"entityRuleId": "DOC.17.0",
"page": 9
},
{
"id": "069397e80191213e72c792f177a22185",
"type": "necropsy_findings",
"entityRuleId": "DOC.17.0",
"page": 9
}
]
}
],
"overridden": false
},
{
"name": "Deviation_from_the_Guideline",
"componentValues": [
{
"value": "There was no deviation from the guidelines and from the Study Plan.\nThere was no deviation from the guidelines and from the Study Plan.",
"originalValue": "There was no deviation from the guidelines and from the Study Plan.\nThere was no deviation from the guidelines and from the Study Plan.",
"valueDescription": "Joining all values of type guideline_deviation with '\n'",
"componentRuleId": "GuidelineDeviation.0.0",
"componentLogEntityReferences": [
{
"id": "4553fb6da48b0c6d6d5d45860c7e473d",
"type": "guideline_deviation",
"entityRuleId": "DOC.11.0",
"page": 3
},
{
"id": "4553fb6da48b0c6d6d5d45860c7e473d",
"type": "guideline_deviation",
"entityRuleId": "DOC.11.0",
"page": 3
}
]
}
],
"overridden": false
},
{
"name": "Conclusion_LD50_Greater_than",
"componentValues": [
{
"value": "",
"originalValue": "",
"valueDescription": "No entity of type 'ld50_greater' found",
"componentRuleId": "Conclusion.1.1",
"componentLogEntityReferences": []
}
],
"overridden": false
},
{
"name": "Conclusion_LD50_mg_per_kg",
"componentValues": [
{
"value": "",
"originalValue": "",
"valueDescription": "Joining all unique values of type with ', '",
"componentRuleId": "Conclusion.0.0",
"componentLogEntityReferences": []
}
],
"overridden": false
},
{
"name": "Conclusion_Minimum_Confidence",
"componentValues": [
{
"value": "",
"originalValue": "",
"valueDescription": "Joining all unique values of type with ', '",
"componentRuleId": "Conclusion.2.0",
"componentLogEntityReferences": []
}
],
"overridden": false
},
{
"name": "Conclusion_Maximum_Confidence",
"componentValues": [
{
"value": "",
"originalValue": "",
"valueDescription": "Joining all unique values of type with ', '",
"componentRuleId": "Conclusion.3.0",
"componentLogEntityReferences": []
}
],
"overridden": false
},
{
"name": "Study_Conclusion",
"componentValues": [
{
"value": "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.",
"originalValue": "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.",
"valueDescription": "Joining all values of type study_conclusion with ' '",
"componentRuleId": "StudyConclusion.0.0",
"componentLogEntityReferences": [
{
"id": "d9c491f44d607083e9e6609b19498d21",
"type": "study_conclusion",
"entityRuleId": "DOC.15.0",
"page": 4
},
{
"id": "d9c491f44d607083e9e6609b19498d21",
"type": "study_conclusion",
"entityRuleId": "DOC.15.0",
"page": 4
}
]
}
],
"overridden": false
}
]
}

View File

@ -4,11 +4,13 @@ import java.util.List;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.FieldDefaults;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE)
@ -16,5 +18,5 @@ public class ComponentLogEntry {
String name;
List<ComponentLogEntryValue> componentValues;
boolean overridden;
}

View File

@ -17,7 +17,6 @@ import lombok.experimental.FieldDefaults;
public class ComponentLogEntryValue {
String value;
String originalValue;
String valueDescription;
String componentRuleId;

View File

@ -0,0 +1,21 @@
package com.iqser.red.service.persistence.service.v1.api.shared.model.component;
import java.util.ArrayList;
import java.util.List;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntry;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ComponentOverrideList {
List<ComponentLogEntry> componentOverrides = new ArrayList<>();
}

View File

@ -1,19 +0,0 @@
package com.iqser.red.service.persistence.service.v1.api.shared.model.component;
import java.util.HashMap;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ComponentsOverrides {
private Map<String, String> componentOverrides = new HashMap<>();
}

View File

@ -0,0 +1,40 @@
package com.iqser.red.service.persistence.service.v1.api.shared.mongo.document;
import java.util.ArrayList;
import java.util.List;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntryValue;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.FieldDefaults;
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@FieldDefaults(level = AccessLevel.PRIVATE)
@Document(collection = "component-log-entries")
public class ComponentDocument {
@Id
@EqualsAndHashCode.Include
String id; // componentLogId/name = componentOverrideId
String componentLogId;
String name;
List<ComponentLogEntryValue> overrideValues = new ArrayList<>();
List<ComponentLogEntryValue> values = new ArrayList<>();
boolean overridden;
}

View File

@ -0,0 +1,38 @@
package com.iqser.red.service.persistence.service.v1.api.shared.mongo.document;
import java.util.ArrayList;
import java.util.List;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Document;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.FieldDefaults;
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@FieldDefaults(level = AccessLevel.PRIVATE)
@Document(collection = "component-logs")
public class ComponentLogDocument {
@Id
@EqualsAndHashCode.Include
String id; // dossierId/fileId = componentLogId
String dossierId;
String fileId;
int analysisNumber;
long componentRulesVersion = -1;
@DBRef
List<ComponentDocument> components = new ArrayList<>();
}

View File

@ -0,0 +1,10 @@
package com.iqser.red.service.persistence.service.v1.api.shared.mongo.exception;
public class DocumentNotFoundException extends RuntimeException {
public DocumentNotFoundException(String errorMessage) {
super(errorMessage);
}
}

View File

@ -1,10 +0,0 @@
package com.iqser.red.service.persistence.service.v1.api.shared.mongo.exception;
public class EntityLogDocumentNotFoundException extends RuntimeException {
public EntityLogDocumentNotFoundException(String errorMessage) {
super(errorMessage);
}
}

View File

@ -0,0 +1,71 @@
package com.iqser.red.service.persistence.service.v1.api.shared.mongo.mapper;
import java.util.ArrayList;
import java.util.List;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLog;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntry;
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.document.ComponentDocument;
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.document.ComponentLogDocument;
@Mapper
public interface ComponentLogDocumentMapper {
ComponentLogDocumentMapper INSTANCE = Mappers.getMapper(ComponentLogDocumentMapper.class);
@Mapping(source = "components", target = "componentLogEntries")
ComponentLog fromComponentLogDocument(ComponentLogDocument componentLogDocument);
default List<ComponentDocument> toComponentDocuments(String dossierId, String fileId, List<ComponentLogEntry> componentLogEntries) {
List<ComponentDocument> componentDocuments = new ArrayList<>();
String componentLogId = getComponentLogId(dossierId, fileId);
for (ComponentLogEntry componentLogEntry : componentLogEntries) {
ComponentDocument componentDocument = new ComponentDocument();
componentDocument.setId(getComponentId(componentLogId, componentLogEntry.getName()));
componentDocument.setComponentLogId(componentLogId);
componentDocument.setName(componentLogEntry.getName());
componentDocument.setOverrideValues(componentLogEntry.getComponentValues());
componentDocuments.add(componentDocument);
}
return componentDocuments;
}
@Mapping(source = "overrideValues", target = "componentValues")
ComponentLogEntry fromComponentDocument(ComponentDocument componentDocument);
@Mapping(expression = "java(getComponentLogId(dossierId, fileId))", target = "id")
@Mapping(expression = "java(toComponentDocuments(dossierId, fileId, componentLog.getComponentLogEntries()))", target = "components")
ComponentLogDocument toComponentLogDocument(String dossierId, String fileId, ComponentLog componentLog);
@Mapping(expression = "java(getComponentId(componentLogId, componentLogEntry.getName()))", target = "id")
@Mapping(source = "componentLogEntry.componentValues", target = "overrideValues")
ComponentDocument toComponentDocument(String componentLogId, ComponentLogEntry componentLogEntry);
default String getComponentLogId(String dossierId, String fileId) {
return dossierId + "/" + fileId;
}
default String getComponentId(String componentLogId, String componentName) {
return componentLogId + "/" + componentName;
}
default String getComponentId(String dossierId, String fileId, String componentName) {
return getComponentLogId(dossierId, fileId) + "/" + componentName;
}
}

View File

@ -0,0 +1,27 @@
package com.iqser.red.service.persistence.service.v1.api.shared.mongo.repository;
import java.util.List;
import java.util.Optional;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.Query;
import org.springframework.stereotype.Repository;
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.document.ComponentDocument;
@Repository
public interface ComponentDocumentRepository extends MongoRepository<ComponentDocument, String> {
@Query(value = "{ 'componentLogId' : ?0}", delete = true)
void deleteByComponentLogId(String componentLogId);
@Query(value = "{ 'componentLogId': ?0, 'componentName': ?1 }")
Optional<ComponentDocument> findComponentDocumentByName(String componentLogId, String componentName);
@Query(value = "{ 'componentLogId': ?0 }")
List<ComponentDocument> findByComponentLogId(String componentLogId);
@Query(value = "{ 'componentLogId': ?0 }", fields = "{ 'overrideValues': 0 }")
List<ComponentDocument> findWithoutOverrideValuesByDossierIdAndFileId(String componentLogId);
}

View File

@ -0,0 +1,24 @@
package com.iqser.red.service.persistence.service.v1.api.shared.mongo.repository;
import java.util.List;
import java.util.Optional;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.Query;
import org.springframework.stereotype.Repository;
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.document.ComponentDocument;
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.document.ComponentLogDocument;
@Repository
public interface ComponentLogDocumentRepository extends MongoRepository<ComponentLogDocument, String> {
@Query(value = "{ 'id' : ?0 }", fields = "{ 'analysisNumber' : 1 }")
Optional<ComponentLogDocument> findAnalysisNumberById(String id);
@Query(value = "{ 'id': ?0 }", fields = "{ 'components': 0 }")
Optional<ComponentLogDocument> findComponentLogDocumentWithoutEntriesById(String id);
}

View File

@ -0,0 +1,227 @@
package com.iqser.red.service.persistence.service.v1.api.shared.mongo.service;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import org.springframework.stereotype.Service;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLog;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntry;
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.document.ComponentDocument;
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.document.ComponentLogDocument;
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.exception.DocumentNotFoundException;
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.mapper.ComponentLogDocumentMapper;
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.repository.ComponentDocumentRepository;
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.repository.ComponentLogDocumentRepository;
import lombok.AccessLevel;
import lombok.experimental.FieldDefaults;
@Service
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
public class ComponentLogMongoService {
ComponentLogDocumentRepository componentLogDocumentRepository;
ComponentDocumentRepository componentDocumentRepository;
ComponentLogDocumentMapper mapper = ComponentLogDocumentMapper.INSTANCE;
public ComponentLogMongoService(ComponentLogDocumentRepository componentLogDocumentRepository, ComponentDocumentRepository componentDocumentRepository) {
this.componentLogDocumentRepository = componentLogDocumentRepository;
this.componentDocumentRepository = componentDocumentRepository;
}
public void insertComponentLog(String dossierId, String fileId, ComponentLog componentLog) {
ComponentLogDocument componentLogDocument = componentLogDocumentRepository.insert(mapper.toComponentLogDocument(dossierId, fileId, componentLog));
componentDocumentRepository.insert(componentLog.getComponentLogEntries()
.stream()
.map(componentLogEntry -> mapper.toComponentDocument(componentLogDocument.getId(), componentLogEntry))
.toList());
}
public void saveComponentLog(String dossierId, String fileId, ComponentLog componentLog) {
ComponentLogDocument componentLogDocument = componentLogDocumentRepository.save(mapper.toComponentLogDocument(dossierId, fileId, componentLog));
componentDocumentRepository.saveAll(componentLog.getComponentLogEntries()
.stream()
.map(componentLogEntry -> mapper.toComponentDocument(componentLogDocument.getId(), componentLogEntry))
.toList());
}
public void upsertComponentLog(String dossierId, String fileId, ComponentLog componentLog) {
Optional<ComponentLogDocument> optionalComponentLogDocument = componentLogDocumentRepository.findById(mapper.getComponentLogId(dossierId, fileId));
if (optionalComponentLogDocument.isEmpty()) {
insertComponentLog(dossierId, fileId, componentLog);
return;
}
ComponentLogDocument oldComponentLogDocument = optionalComponentLogDocument.get();
List<ComponentDocument> oldComponentDocuments = oldComponentLogDocument.getComponents();
ComponentLogDocument newComponentLogDocument = mapper.toComponentLogDocument(dossierId, fileId, componentLog);
List<ComponentDocument> newComponentDocuments = newComponentLogDocument.getComponents();
List<ComponentDocument> toUpdate = new ArrayList<>(newComponentDocuments);
toUpdate.retainAll(oldComponentDocuments);
List<ComponentDocument> toRemove = new ArrayList<>(oldComponentDocuments);
toRemove.removeAll(toUpdate);
List<ComponentDocument> toInsert = new ArrayList<>(newComponentDocuments);
toInsert.removeAll(toUpdate);
componentDocumentRepository.saveAll(toUpdate);
componentDocumentRepository.deleteAll(toRemove);
componentDocumentRepository.insert(toInsert);
componentLogDocumentRepository.save(newComponentLogDocument);
}
public void deleteComponentLog(String dossierId, String fileId) {
String componentLogId = mapper.getComponentLogId(dossierId, fileId);
componentLogDocumentRepository.deleteById(componentLogId);
componentDocumentRepository.deleteByComponentLogId(componentLogId);
}
public void insertComponentLogEntries(String dossierId, String fileId, List<ComponentLogEntry> componentLogEntries) {
String componentLogId = mapper.getComponentLogId(dossierId, fileId);
List<ComponentDocument> componentDocuments = componentLogEntries.stream()
.map(componentLogEntry -> mapper.toComponentDocument(componentLogId, componentLogEntry))
.toList();
componentDocumentRepository.insert(componentDocuments);
}
private ComponentLogDocument getComponentLogDocument(String componentLogId) {
return componentLogDocumentRepository.findById(componentLogId)
.orElseThrow(() -> new DocumentNotFoundException(String.format("Component log not found for %s", componentLogId)));
}
public void saveComponentLogEntries(String dossierId, String fileId, List<ComponentLogEntry> componentLogEntries) {
String componentLogId = mapper.getComponentLogId(dossierId, fileId);
List<ComponentDocument> componentDocuments = componentLogEntries.stream()
.map(componentLogEntry -> mapper.toComponentDocument(componentLogId, componentLogEntry))
.toList();
componentDocumentRepository.saveAll(componentDocuments);
}
public void updateComponentLogEntries(String dossierId, String fileId, List<ComponentLogEntry> componentLogEntries) {
String componentLogId = mapper.getComponentLogId(dossierId, fileId);
componentDocumentRepository.saveAll(componentLogEntries.stream()
.map(componentLogEntry -> mapper.toComponentDocument(componentLogId, componentLogEntry))
.toList());
}
public void deleteComponentLogEntriesFromComponentLog(String dossierId, String fileId, List<ComponentLogEntry> componentLogEntries) {
String componentLogId = mapper.getComponentLogId(dossierId, fileId);
ComponentLogDocument componentLogDocument = getComponentLogDocument(componentLogId);
List<ComponentDocument> componentDocuments = componentLogEntries.stream()
.map(componentLogEntry -> mapper.toComponentDocument(componentLogId, componentLogEntry))
.toList();
componentLogDocument.getComponents().removeAll(componentDocuments);
componentDocumentRepository.deleteAll(componentDocuments);
componentLogDocumentRepository.save(componentLogDocument);
}
public Optional<ComponentLog> findComponentLogByDossierIdAndFileId(String dossierId, String fileId) {
return componentLogDocumentRepository.findById(mapper.getComponentLogId(dossierId, fileId))
.map(mapper::fromComponentLogDocument);
}
public Optional<ComponentLogEntry> findComponentLogEntryById(String dossierId, String fileId, String componentName) {
return componentDocumentRepository.findById(mapper.getComponentId(mapper.getComponentLogId(dossierId, fileId), componentName))
.map(mapper::fromComponentDocument);
}
public List<ComponentLogEntry> findOverrides(String dossierId, String fileId) {
String componentLogId = mapper.getComponentLogId(dossierId, fileId);
return componentDocumentRepository.findByComponentLogId(componentLogId)
.stream()
.map(mapper::fromComponentDocument)
.peek(componentLogEntry -> componentLogEntry.setOverridden(true))
.toList();
}
public boolean componentLogDocumentExists(String dossierId, String fileId) {
return componentLogDocumentRepository.existsById(mapper.getComponentLogId(dossierId, fileId));
}
public Optional<Integer> findLatestAnalysisNumber(String dossierId, String fileId) {
return componentLogDocumentRepository.findAnalysisNumberById(mapper.getComponentLogId(dossierId, fileId))
.map(ComponentLogDocument::getAnalysisNumber);
}
public List<ComponentLogEntry> findComponentLogEntriesWithComponentNameIn(String dossierId, String fileId, Collection<String> componentNames) {
String componentLogId = mapper.getComponentLogId(dossierId, fileId);
List<String> names = componentNames.stream()
.map(name -> mapper.getComponentId(componentLogId, name))
.toList();
return componentDocumentRepository.findAllById(names)
.stream()
.map(mapper::fromComponentDocument)
.toList();
}
public Optional<ComponentLog> findComponentLogWithoutEntries(String dossierId, String fileId) {
return componentLogDocumentRepository.findComponentLogDocumentWithoutEntriesById(mapper.getComponentLogId(dossierId, fileId))
.map(mapper::fromComponentLogDocument);
}
public void deleteOverrides(String dossierId, String fileId, String componentName) {
String componentId = mapper.getComponentId(dossierId, fileId, componentName);
componentDocumentRepository.deleteById(componentId);
}
}

View File

@ -13,7 +13,7 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntityLogEntry;
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.document.EntityLogDocument;
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.document.EntityLogEntryDocument;
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.exception.EntityLogDocumentNotFoundException;
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.exception.DocumentNotFoundException;
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.mapper.EntityLogDocumentMapper;
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.repository.EntityLogDocumentRepository;
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.repository.EntityLogEntryDocumentRepository;
@ -167,7 +167,7 @@ public class EntityLogMongoService {
Optional<EntityLogDocument> optionalEntityLogDocument = entityLogDocumentRepository.findById(entityLogId);
if (optionalEntityLogDocument.isEmpty()) {
throw new EntityLogDocumentNotFoundException(String.format("Entity log not found for %s", entityLogId));
throw new DocumentNotFoundException(String.format("Entity log not found for %s", entityLogId));
}
return optionalEntityLogDocument.get();