diff --git a/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/DossierTemplateControllerV2.java b/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/DossierTemplateControllerV2.java index 1376d1d60..145bb8012 100644 --- a/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/DossierTemplateControllerV2.java +++ b/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/DossierTemplateControllerV2.java @@ -1,5 +1,7 @@ package com.iqser.red.persistence.service.v2.external.api.impl.controller; +import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.EXPERIMENTAL; +import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.READ_FILE_ATTRIBUTES_CONFIG; import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.READ_RULES; import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.WRITE_RULES; @@ -21,6 +23,7 @@ import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import com.iqser.red.persistence.service.v1.external.api.impl.controller.DossierTemplateController; +import com.iqser.red.persistence.service.v1.external.api.impl.controller.FileAttributesController; import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException; import com.iqser.red.service.persistence.management.v1.processor.service.RulesValidationService; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService; @@ -31,6 +34,8 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierTemp import com.iqser.red.service.persistence.service.v1.api.shared.model.RuleFileType; import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.RulesUploadRequest; +import com.iqser.red.service.persistence.service.v2.api.external.model.FileAttributeDefinition; +import com.iqser.red.service.persistence.service.v2.api.external.model.FileAttributeDefinitionList; import com.iqser.red.service.persistence.service.v2.api.external.model.RulesValidationMessage; import com.iqser.red.service.persistence.service.v2.api.external.model.RulesValidationResponse; import com.iqser.red.service.persistence.service.v2.api.external.resource.DossierTemplateResource; @@ -55,6 +60,7 @@ public class DossierTemplateControllerV2 implements DossierTemplateResource { private final RulesPersistenceService rulesPersistenceService; private final RulesValidationService rulesValidationService; private final AuditPersistenceService auditPersistenceService; + private final FileAttributesController fileAttributesController; public List getAllDossierTemplates() { @@ -69,7 +75,7 @@ public class DossierTemplateControllerV2 implements DossierTemplateResource { } - @PreAuthorize("hasAuthority('" + WRITE_RULES + "')") + @PreAuthorize("hasAuthority('" + WRITE_RULES + "') and hasAuthority('" + EXPERIMENTAL + "')") public ResponseEntity uploadEntityRules(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, @Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file, @Parameter(name = DRY_RUN_PARAM, description = "If true rules will be only validated not stored.") @RequestParam(value = DRY_RUN_PARAM, required = false, defaultValue = "false") boolean dryRun) { @@ -85,7 +91,7 @@ public class DossierTemplateControllerV2 implements DossierTemplateResource { } - @PreAuthorize("hasAuthority('" + WRITE_RULES + "')") + @PreAuthorize("hasAuthority('" + WRITE_RULES + "') and hasAuthority('" + EXPERIMENTAL + "')") public ResponseEntity uploadComponentRules(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, @Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file, @Parameter(name = DRY_RUN_PARAM, description = "If true rules will be only validated not stored.") @RequestParam(value = DRY_RUN_PARAM, required = false, defaultValue = "false") boolean dryRun) { @@ -101,6 +107,27 @@ public class DossierTemplateControllerV2 implements DossierTemplateResource { return downloadRules(dossierTemplateId, RuleFileType.COMPONENT); } + @PreAuthorize("hasAuthority('" + READ_FILE_ATTRIBUTES_CONFIG + "')") + public FileAttributeDefinitionList getFileAttributeDefinitions(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId) { + + var fileAttributeConfigs = fileAttributesController.getFileAttributesConfiguration(dossierTemplateId); + + return new FileAttributeDefinitionList(fileAttributeConfigs.getFileAttributeConfigs().stream() + .map(fileAttributeConfig -> FileAttributeDefinition.builder() + .id(fileAttributeConfig.getId()) + .name(fileAttributeConfig.getLabel()) + .type(fileAttributeConfig.getType()) + .mappedCsvColumnHeader(fileAttributeConfig.getCsvColumnHeader()) + .reportingPlaceholder(fileAttributeConfig.getPlaceholder()) + .displaySettings(FileAttributeDefinition.DisplaySettings.builder() + .primaryAttribute(fileAttributeConfig.isPrimaryAttribute()) + .editable(fileAttributeConfig.isEditable()) + .filterable(fileAttributeConfig.isFilterable()) + .displayedInFileList(fileAttributeConfig.isDisplayedInFileList()) + .build()) + .build()) + .toList()); + } @SneakyThrows private ResponseEntity uploadRules(String dossierTemplateId, RuleFileType ruleFileType, MultipartFile file, boolean dryRun) { diff --git a/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/FileAttributeDefinition.java b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/FileAttributeDefinition.java new file mode 100644 index 000000000..facf157fa --- /dev/null +++ b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/FileAttributeDefinition.java @@ -0,0 +1,34 @@ +package com.iqser.red.service.persistence.service.v2.api.external.model; + +import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileAttributeType; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class FileAttributeDefinition { + + private String id; + private String name; + private FileAttributeType type = FileAttributeType.TEXT; + + private String mappedCsvColumnHeader; + private String reportingPlaceholder; + private DisplaySettings displaySettings; + + @Data + @NoArgsConstructor + @AllArgsConstructor + @Builder + public static class DisplaySettings { + private boolean primaryAttribute; + private boolean editable; + private boolean filterable; + private boolean displayedInFileList; + } +} diff --git a/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/FileAttributeDefinitionList.java b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/FileAttributeDefinitionList.java new file mode 100644 index 000000000..0cb14fc28 --- /dev/null +++ b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/FileAttributeDefinitionList.java @@ -0,0 +1,19 @@ +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 FileAttributeDefinitionList { + + @Builder.Default + private List fileAttributeDefinitions = new ArrayList<>(); +} diff --git a/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/DossierTemplateResource.java b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/DossierTemplateResource.java index e2dbdb738..616e76bb1 100644 --- a/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/DossierTemplateResource.java +++ b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/DossierTemplateResource.java @@ -2,6 +2,7 @@ package com.iqser.red.service.persistence.service.v2.api.external.resource; import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierTemplateModel; import com.iqser.red.service.persistence.service.v1.api.shared.model.RuleFileType; +import com.iqser.red.service.persistence.service.v2.api.external.model.FileAttributeDefinitionList; import com.iqser.red.service.persistence.service.v2.api.external.model.RulesValidationResponse; import io.swagger.v3.oas.annotations.Operation; @@ -34,6 +35,8 @@ public interface DossierTemplateResource { String ENTITY_RULES_PATH = "/entity-rules"; String COMPONENT_RULES_PATH = "/component-rules"; + + String FILE_ATTRIBUTE_DEFINITIONS_PATH = "/file-attribute-definitions"; String DOSSIER_TEMPLATE_ID_PARAM = "dossierTemplateId"; String DOSSIER_TEMPLATE_ID_PATH_VARIABLE = "/{" + DOSSIER_TEMPLATE_ID_PARAM + "}"; @@ -86,5 +89,8 @@ public interface DossierTemplateResource { @GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_RULES_PATH, produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) ResponseEntity downloadComponentRules(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, @PathVariable(RULE_FILE_TYPE_PARAMETER_NAME) RuleFileType ruleFileType); - + @Operation(summary = "Get the file attribute definitions of a DossierTemplate.", description = "None") + @GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + FILE_ATTRIBUTE_DEFINITIONS_PATH, produces = MediaType.APPLICATION_JSON_VALUE) + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "File attribute definitions returned successfully."), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.")}) + FileAttributeDefinitionList getFileAttributeDefinitions(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId); } diff --git a/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/openapi.yaml b/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/openapi.yaml index b13f5f3f5..6b38199ef 100644 --- a/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/openapi.yaml +++ b/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/openapi.yaml @@ -272,6 +272,26 @@ paths: $ref: '#/components/responses/429' "500": $ref: '#/components/responses/500' + /api/dossier-templates/{dossierTemplateId}/file-attribute-definitions: + get: + summary: Returns the list of all existing file attribute definitions + tags: + - 1. Dossier Templates + description: | + Retrieves a collection of file attribute definitions associated with a specific dossier template. Each file + attribute definition includes details such as attribute type, name, and other relevant metadata. This endpoint + is useful for clients needing to understand what attributes are expected or allowed for files under a particular + dossier template. + parameters: + - $ref: '#/components/parameters/dossierTemplateId' + responses: + "200": + content: + '*/*': + schema: + $ref: '#/components/schemas/FileAttributeDefinitionList' + description: | + Successfully returned the file attribute definitions for the specified dossier template. /api/dossier-templates/{dossierTemplateId}/dossiers: get: operationId: getDossiers @@ -1377,6 +1397,120 @@ components: entityRuleId: DEF.13.37 type: another_entity_type page: 456 + FileAttributeDefinition: + type: object + description: | + The `FileAttributeDefinition` object contains the relevant information to define a file attribute. File attributes + are used to manage additional meta-data of files. + properties: + id: + type: string + format: uuid + description: | + A unique identifier for the file attribute definition. This ID is automatically generated by + the system upon creation and is used for referencing the file attribute definition in API calls. + name: + type: string + description: | + User-defined name of the file attribute definition, capturing its essence. The name needs to be unique + for the dossier template. + type: + type: string + enum: + - TEXT + - NUMBER + - DATE + description: | + Determines the type of the dossier attribute's value. Please note that currently the system + does not validate the values against this definition. This is just a hint for a user interface + that needs to handle invalid entries. Possible values for the type: + - `TEXT`: The value is just a string, i.e., any sequence of characters. + - `NUMBER`: The value is a string expressing a number, with or without decimals. + - `DATE`: The value is a string expressing a date information. + mappedCsvColumnHeader: + type: string + description: | + The name of a CSV column. When importing a CSV file with additional file information the system gets the value from the respective CSV column. + reportingPlaceholder: + type: string + description: | + The name of the placeholder of the file attribute that can be used in report templates. The placeholder follows a specific format convention: + `{{file.attribute.}}` while the name transformed into 'PascalCase' and does not contain whitespaces. The placeholder is unique in a dossier template. + displaySettings: + $ref: '#/components/schemas/FileAttributeDisplaySettings' + required: + - name + - type + example: + id: "123e4567-e89b-12d3-a456-426614174000" + name: "Document Type" + type: "TEXT" + mappedCsvColumnHeader: "DocumentCategory" + reportingPlaceholder: "{{file.attribute.DocumentType}}" + displaySettings: + primaryAttribute: true + editable: true + filterable: true + displayedInFileList: false + FileAttributeDisplaySettings: + type: object + description: | + Display setting for the RedactManager and DocuMine user interface. These settings control how the UI handles and presents the file attributes. + properties: + primaryAttribute: + type: boolean + description: | + If true, the RedactManager and DocuMine user interfaces show the value of the file attribute in the file list below the file name. + editable: + type: boolean + description: | + If true, the RedactManager and DocuMine user interfaces allow manual editing of the value. Otherwise only importing and setting by rules would be possible. + filterable: + type: boolean + description: | + If true, the RedactManager and DocuMine user interfaces add filter options to the file list. + displayedInFileList: + type: boolean + description: | + if true, the RedactManager and DocuMine user interfaces show the values in the file list. + required: + - primaryAttribute + - editable + - filterable + - displayedInFileList + example: : + primaryAttribute: false + editable: true + filterable: true + displayedInFileList: false + FileAttributeDefinitionList: + type: object + description: A list of file attribute definitions. + properties: + fileAttributeDefinitions: + items: + $ref: '#/components/schemas/FileAttributeDefinition' + type: array + example: + - id: "123e4567-e89b-12d3-a456-426614174000" + name: "Document Type" + type: "TEXT" + mappedCsvColumnHeader: "DocumentCategory" + reportingPlaceholder: "{{file.attribute.DocumentType}}" + displaySettings: + primaryAttribute: true + editable: true + filterable: true + displayedInFileList: false + - id: "23e45678-e90b-12d3-a456-765114174321" + name: "Comment" + type: "TEXT" + reportingPlaceholder: "{{file.attribute.Comment}}" + displaySettings: + primaryAttribute: false + editable: true + filterable: false + displayedInFileList: false Dossier: type: object description: | @@ -1385,6 +1519,7 @@ components: properties: id: type: string + format: uuid description: | A unique identifier for the dossier. This ID is automatically generated by the system upon creation and is used for referencing the dossier in API calls. diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/roles/ActionRoles.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/roles/ActionRoles.java index 6ccc244e2..73b62c34f 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/roles/ActionRoles.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/roles/ActionRoles.java @@ -2,6 +2,9 @@ package com.iqser.red.service.persistence.management.v1.processor.roles; public final class ActionRoles { + // Experimental + public static final String EXPERIMENTAL = "red-experimental"; + // Audit public static final String SEARCH_AUDIT_LOG = "red-search-audit-log";