Merge branch 'RED-7578' into 'master'

RED-7578: Implemented logic of components endpoint for api v2

Closes RED-7578

See merge request redactmanager/persistence-service!126
This commit is contained in:
Dominique Eifländer 2023-09-15 10:56:14 +02:00
commit f4b2ec88d6
18 changed files with 120 additions and 25 deletions

View File

@ -7,6 +7,7 @@ import com.iqser.red.service.persistence.management.v1.processor.exception.Confl
import com.iqser.red.service.persistence.management.v1.processor.exception.NotAllowedException;
import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException;
import com.knecon.fforesight.tenantcommons.TenantContext;
import com.mchange.rmi.NotAuthorizedException;
import io.swagger.v3.oas.annotations.Hidden;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -67,7 +68,6 @@ public class ExternalControllerAdvice {
return new ErrorMessage(OffsetDateTime.now(), e.getMessage());
}
@Hidden
@ResponseBody
@ResponseStatus(value = HttpStatus.FORBIDDEN)
@ExceptionHandler({AccessDeniedException.class})
@ -76,7 +76,14 @@ public class ExternalControllerAdvice {
return new ErrorMessage(OffsetDateTime.now(), e.getMessage());
}
@Hidden
@ResponseBody
@ResponseStatus(value = HttpStatus.UNAUTHORIZED)
@ExceptionHandler({NotAuthorizedException.class})
public ErrorMessage handleNotAuthorizedException(NotAuthorizedException e) {
return new ErrorMessage(OffsetDateTime.now(), e.getMessage());
}
@ResponseBody
@ResponseStatus(value = HttpStatus.FORBIDDEN)
@ExceptionHandler({NotAllowedException.class})

View File

@ -1,34 +1,102 @@
package com.iqser.red.persistence.service.v2.external.api.impl.controller;
import com.iqser.red.persistence.service.v1.external.api.impl.controller.AnalysisLogController;
import com.iqser.red.persistence.service.v1.external.api.impl.controller.DossierTemplateController;
import com.iqser.red.persistence.service.v1.external.api.impl.controller.StatusController;
import com.iqser.red.service.persistence.management.v1.processor.service.ComponentOverrideService;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentEntityReference;
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.v2.api.external.model.Component;
import com.iqser.red.service.persistence.service.v2.api.external.model.Entity;
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;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.*;
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;
@RestController
@Tag(name = "4. component endpoints", description = "Provides operations related to components")
@RequiredArgsConstructor
@Tag(name = "4. Component endpoints", description = "Provides operations related to components")
public class ComponentControllerV2 implements ComponentResource {
private final DossierTemplateController dossierTemplateController;
private final AnalysisLogController analysisLogController;
private final StatusController statusController;
private final ComponentOverrideService componentOverrideService;
@Override
public FileComponents getComponents(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@PathVariable(DOSSIER_ID_PARAM) String dossierId,
@PathVariable(FILE_ID_PARAM) String fileId,
@RequestParam(name = INCLUDE_DETAILS_PARAM, defaultValue = "false", required = false) boolean includeDetails) {
return null;
dossierTemplateController.getDossierTemplate(dossierTemplateId);
var componentLog = analysisLogController.getComponentLog(dossierId, fileId);
var overrides = componentOverrideService.getOverrides(dossierId, fileId);
Map<String, List<String>> components = new HashMap<>();
componentLog.getComponentLogCategories().forEach(c -> {
if (overrides.getComponentOverrides().containsKey(c.getCategory())) {
components.computeIfAbsent(c.getCategory(), (x) -> new ArrayList<>()).add(overrides.getComponentOverrides().get(c.getCategory()));
} else {
components.computeIfAbsent(c.getCategory(), (x) -> new ArrayList<>()).addAll(c.getComponentLogEntries().stream().map(ComponentLogEntry::getValue).toList());
}
});
Map<String, List<Component>> componentsDetails = new HashMap<>();
if (includeDetails) {
componentLog.getComponentLogCategories().forEach(c -> {
componentsDetails.computeIfAbsent(c.getCategory(), (x) -> new ArrayList<>()).addAll(c.getComponentLogEntries().stream().map(entry -> convert(entry, overrides, c.getCategory())).toList());
});
}
return FileComponents.builder().dossierTemplateId(dossierTemplateId).dossierId(dossierId).fileId(fileId).components(components).componentDetails(componentsDetails).build();
}
private Component convert(ComponentLogEntry componentLogEntry, ComponentsOverrides overrides, String category) {
return Component.builder()
.componentRule(componentLogEntry.getMatchedRule())
.entityReferences(componentLogEntry.getComponentEntityReferences().stream().map(this::convertComponentEntityReference).toList())
.originalValues(List.of(componentLogEntry.getValue()))
.values(overrides.getComponentOverrides().containsKey(category) ? List.of(overrides.getComponentOverrides().get(category)) : List.of(componentLogEntry.getValue()))
.build();
}
private Entity convertComponentEntityReference(ComponentEntityReference componentEntityReference) {
return Entity.builder()
.id(componentEntityReference.getId())
.entityRule(componentEntityReference.getRuleIdentifier())
.type(componentEntityReference.getType())
.pages(Set.of(componentEntityReference.getPage()))
.value(componentEntityReference.getValue())
.build();
}
@Override
public FileComponents getComponentsOfDossier(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@PathVariable(DOSSIER_ID_PARAM) String dossierId,
@RequestParam(name = INCLUDE_DETAILS_PARAM, defaultValue = "false", required = false) boolean includeDetails) {
return null;
public FileComponentsList getComponentsOfDossier(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@PathVariable(DOSSIER_ID_PARAM) String dossierId,
@RequestParam(name = INCLUDE_DETAILS_PARAM, defaultValue = "false", required = false) boolean includeDetails) {
dossierTemplateController.getDossierTemplate(dossierTemplateId);
var dossierFiles = statusController.getDossierStatus(dossierId);
return new FileComponentsList(dossierFiles.stream().map(file -> getComponents(dossierTemplateId, dossierId, file.getFileId(), includeDetails)).toList());
}

View File

@ -21,7 +21,7 @@ import static com.iqser.red.service.persistence.service.v2.api.external.resource
@RestController
@RequiredArgsConstructor
@Tag(name = "2. dossier endpoints", description = "Provides operations related to dossiers")
@Tag(name = "2. Dossier endpoints", description = "Provides operations related to dossiers")
public class DossierControllerV2 implements DossierResource {
private final DossierTemplateController dossierTemplateController;

View File

@ -12,7 +12,7 @@ import java.util.List;
@RestController
@RequiredArgsConstructor
@Tag(name = "1. dossier templates endpoints", description = "Provides operations related to dossier templates")
@Tag(name = "1. Dossier templates endpoints", description = "Provides operations related to dossier templates")
public class DossierTemplateControllerV2 implements DossierTemplateResource {
private final DossierTemplateController dossierTemplateController;

View File

@ -23,7 +23,7 @@ import static com.iqser.red.service.persistence.service.v2.api.external.resource
@RestController
@RequiredArgsConstructor
@Tag(name = "3. file endpoints", description = "Provides operations related to files")
@Tag(name = "3. File endpoints", description = "Provides operations related to files")
public class FileControllerV2 implements FileResource {
private final UploadController uploadController;

View File

@ -11,7 +11,7 @@ import org.springframework.web.bind.annotation.RestController;
@RestController
@RequiredArgsConstructor
@Tag(name = "5. license endpoints", description = "Provides operations related to the license")
@Tag(name = "5. License endpoints", description = "Provides operations related to the license")
public class LicenseControllerV2 implements LicenseResource {
private final LicenseReportController licenseReportController;

View File

@ -1,5 +1,6 @@
package com.iqser.red.service.persistence.service.v2.api.external.model;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlCData;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
@ -14,9 +15,13 @@ import java.util.List;
@AllArgsConstructor
public class Component {
@JacksonXmlCData
private String name;
@JacksonXmlCData
private List<String> values;
@JacksonXmlCData
private List<String> originalValues;
@JacksonXmlCData
private String componentRule;
@Builder.Default

View File

@ -1,11 +1,13 @@
package com.iqser.red.service.persistence.service.v2.api.external.model;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlCData;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.*;
import java.util.HashSet;
import java.util.Set;
@Data
@Builder
@ -14,14 +16,13 @@ import java.util.*;
public class Entity {
private String id;
@JacksonXmlCData
private String type;
private String value;
@JacksonXmlCData
private String entityRule;
@JacksonXmlCData
private String value;
@Builder.Default
private Set<Integer> pages = new HashSet<>();
@Builder.Default
private Map<String, List<String>> components = new HashMap<>();
}

View File

@ -1,5 +1,6 @@
package com.iqser.red.service.persistence.service.v2.api.external.model;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlCData;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
@ -18,11 +19,15 @@ public class FileComponents {
private String dossierTemplateId;
private String dossierId;
private String fileId;
@JacksonXmlCData
private String filename;
@Builder.Default
@JacksonXmlCData
private Map<String, List<String>> components = new HashMap<>();
private Component componentDetails;
@Builder.Default
@JacksonXmlCData
private Map<String, List<Component>> componentDetails = new HashMap<>();
}

View File

@ -1,6 +1,7 @@
package com.iqser.red.service.persistence.service.v2.api.external.resource;
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.responses.ApiResponse;
@ -16,6 +17,7 @@ import static com.iqser.red.service.persistence.service.v2.api.external.resource
import static com.iqser.red.service.persistence.service.v2.api.external.resource.DossierTemplateResource.*;
import static com.iqser.red.service.persistence.service.v2.api.external.resource.FileResource.*;
@ApiResponses(value = {@ApiResponse(responseCode = "429", description = "Too many requests.")})
@ResponseStatus(value = HttpStatus.OK)
public interface ComponentResource {
@ -40,9 +42,9 @@ public interface ComponentResource {
@GetMapping(value = PATH + BULK_COMPONENTS_PATH, produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
@Operation(summary = "Returns the components for all files of a dossier", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
FileComponents getComponentsOfDossier(@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 = INCLUDE_DETAILS_PARAM, description = "TODO") @RequestParam(name = INCLUDE_DETAILS_PARAM, defaultValue = "false", required = false) boolean includeDetails);
FileComponentsList getComponentsOfDossier(@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 = INCLUDE_DETAILS_PARAM, description = "TODO") @RequestParam(name = INCLUDE_DETAILS_PARAM, defaultValue = "false", required = false) boolean includeDetails);
}

View File

@ -14,6 +14,7 @@ import org.springframework.web.bind.annotation.*;
import static com.iqser.red.service.persistence.service.v2.api.external.resource.DossierTemplateResource.*;
@ApiResponses(value = {@ApiResponse(responseCode = "429", description = "Too many requests.")})
public interface DossierResource {
String DOSSIER_PATH = "/dossiers";
@ -52,7 +53,7 @@ public interface DossierResource {
@ResponseBody
@PostMapping(value = PATH, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Creates or updates a dossier for a specific dossier template.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "201", description = "Successfully saved the dossier."), @ApiResponse(responseCode = "400", description = "Incorrect dossier ID provided or attempted to change dossier-template for a dossier with files."), @ApiResponse(responseCode = "409", description = "Duplicate")})
@ApiResponses(value = {@ApiResponse(responseCode = "201", description = "Successfully saved the dossier."), @ApiResponse(responseCode = "400", description = "Malformed request parameters or body"), @ApiResponse(responseCode = "409", description = "Duplicate")})
ResponseEntity<Dossier> createDossierOrUpdateDossier(@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,
@RequestBody DossierRequest dossier);

View File

@ -12,6 +12,7 @@ import org.springframework.web.bind.annotation.ResponseStatus;
import java.util.List;
@ApiResponses(value = {@ApiResponse(responseCode = "429", description = "Too many requests.")})
@ResponseStatus(HttpStatus.OK)
public interface DossierTemplateResource {

View File

@ -17,6 +17,7 @@ import org.springframework.web.multipart.MultipartFile;
import static com.iqser.red.service.persistence.service.v2.api.external.resource.DossierResource.*;
import static com.iqser.red.service.persistence.service.v2.api.external.resource.DossierTemplateResource.*;
@ApiResponses(value = {@ApiResponse(responseCode = "429", description = "Too many requests.")})
public interface FileResource {
String FILE_PATH = "/files";

View File

@ -12,6 +12,7 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
@ApiResponses(value = {@ApiResponse(responseCode = "429", description = "Too many requests.")})
public interface LicenseResource {
String LICENSE_PATH = "/license";

View File

@ -109,6 +109,7 @@ public class InternalControllerAdvice {
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public ErrorMessage handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
var errorList = e.getFieldErrors();
String errorListAsString = errorList.stream().map(fieldError -> fieldError.getField() + ": " + fieldError.getDefaultMessage()).collect(Collectors.joining(", "));
return new ErrorMessage(OffsetDateTime.now(), String.format("You have empty/wrong formatted parameters: %s", errorListAsString));

View File

@ -9,6 +9,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
@ -17,6 +18,7 @@ import java.io.IOException;
@RestController
@RequiredArgsConstructor
@ConditionalOnProperty(value = "fforesight.springdoc.enabled", havingValue = "true")
@RequestMapping("${fforesight.springdoc.base-path:}")
public class SpringDocRedirectController {
@Value("${server.servlet.context-path:}")

View File

@ -244,7 +244,7 @@ public abstract class AbstractPersistenceServerServiceTest {
when(amqpAdmin.getQueueInfo(Mockito.any())).thenReturn(null);
when(redactionLogService.getRedactionLog(Mockito.any(), Mockito.any())).thenReturn(new RedactionLog(1, 1, Lists.newArrayList(), null, 0, 0, 0, 0));
when(redactionClient.testRules(Mockito.any())).thenReturn(DroolsSyntaxValidation.builder().compiled(true).droolsSyntaxErrorMessages(Collections.emptyList()).build());
when(redactionClient.testRules(Mockito.any())).thenReturn(DroolsSyntaxValidation.builder().droolsSyntaxErrorMessages(Collections.emptyList()).build());
}

View File

@ -31,7 +31,7 @@
</modules>
<properties>
<redaction-service.version>4.122.0</redaction-service.version>
<redaction-service.version>4.126.0</redaction-service.version>
<search-service.version>2.71.0</search-service.version>
<pdftron-redaction-service.version>4.29.0</pdftron-redaction-service.version>
<redaction-report-service.version>4.13.0</redaction-report-service.version>