diff --git a/.gitignore b/.gitignore index 92322c4..2279178 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .idea/ target/ +logs/ diff --git a/core/pom.xml b/core/pom.xml index 38783d5..b57179d 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -50,6 +50,16 @@ jsoup 1.17.2 + + gr.cite + logging + 2.2.0 + + + gr.cite + exceptions + 2.2.0 + com.fasterxml.jackson.dataformat jackson-dataformat-xml diff --git a/core/src/main/java/org/opencdmp/filetransformer/docx/audit/AuditableAction.java b/core/src/main/java/org/opencdmp/filetransformer/docx/audit/AuditableAction.java new file mode 100644 index 0000000..efeee57 --- /dev/null +++ b/core/src/main/java/org/opencdmp/filetransformer/docx/audit/AuditableAction.java @@ -0,0 +1,15 @@ +package org.opencdmp.filetransformer.docx.audit; + + +import gr.cite.tools.logging.EventId; + +public class AuditableAction { + + public static final EventId FileTransformer_ExportDmp = new EventId(1000, "FileTransformer_ExportDmp"); + public static final EventId FileTransformer_ExportDescription = new EventId(1001, "FileTransformer_ExportDescription"); + public static final EventId FileTransformer_ImportFileToDmp = new EventId(1002, "FileTransformer_ImportFileToDmp"); + public static final EventId FileTransformer_ImportFileToDescription = new EventId(1003, "FileTransformer_ImportFileToDescription"); + public static final EventId FileTransformer_GetSupportedFormats = new EventId(1004, "FileTransformer_GetSupportedFormats"); + + +} diff --git a/core/src/main/java/org/opencdmp/filetransformer/docx/service/pid/PidServiceImpl.java b/core/src/main/java/org/opencdmp/filetransformer/docx/service/pid/PidServiceImpl.java index e727173..e99ff25 100644 --- a/core/src/main/java/org/opencdmp/filetransformer/docx/service/pid/PidServiceImpl.java +++ b/core/src/main/java/org/opencdmp/filetransformer/docx/service/pid/PidServiceImpl.java @@ -1,6 +1,7 @@ package org.opencdmp.filetransformer.docx.service.pid; import com.fasterxml.jackson.databind.ObjectMapper; +import gr.cite.tools.logging.LoggerService; import org.opencdmp.filetransformer.docx.service.wordfiletransformer.WordFileTransformerServiceProperties; import org.opencdmp.filetransformer.docx.model.PidLink; import org.slf4j.Logger; @@ -13,7 +14,7 @@ import java.util.List; @Component public class PidServiceImpl implements PidService { - private static final Logger logger = LoggerFactory.getLogger(PidServiceImpl.class); + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(PidServiceImpl.class)); private final WordFileTransformerServiceProperties properties; private final ObjectMapper objectMapper = new ObjectMapper(); private List pidLinks = null; diff --git a/core/src/main/java/org/opencdmp/filetransformer/docx/service/storage/FileStorageServiceImpl.java b/core/src/main/java/org/opencdmp/filetransformer/docx/service/storage/FileStorageServiceImpl.java index 2aec273..a529d8a 100644 --- a/core/src/main/java/org/opencdmp/filetransformer/docx/service/storage/FileStorageServiceImpl.java +++ b/core/src/main/java/org/opencdmp/filetransformer/docx/service/storage/FileStorageServiceImpl.java @@ -1,5 +1,7 @@ package org.opencdmp.filetransformer.docx.service.storage; +import gr.cite.tools.logging.LoggerService; +import org.opencdmp.filetransformer.docx.service.wordfiletransformer.WordFileTransformerService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -16,7 +18,7 @@ import java.util.UUID; @Service public class FileStorageServiceImpl implements FileStorageService { - private final static Logger logger = LoggerFactory.getLogger(FileStorageServiceImpl.class); + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(WordFileTransformerService.class)); private final FileStorageServiceProperties properties; diff --git a/core/src/main/java/org/opencdmp/filetransformer/docx/service/wordfiletransformer/WordFileTransformerService.java b/core/src/main/java/org/opencdmp/filetransformer/docx/service/wordfiletransformer/WordFileTransformerService.java index 3397244..e97bbe3 100644 --- a/core/src/main/java/org/opencdmp/filetransformer/docx/service/wordfiletransformer/WordFileTransformerService.java +++ b/core/src/main/java/org/opencdmp/filetransformer/docx/service/wordfiletransformer/WordFileTransformerService.java @@ -1,5 +1,8 @@ package org.opencdmp.filetransformer.docx.service.wordfiletransformer; +import gr.cite.tools.exception.MyApplicationException; +import gr.cite.tools.exception.MyApplicationException; +import gr.cite.tools.logging.LoggerService; import org.opencdmp.commonmodels.enums.DescriptionStatus; import org.opencdmp.commonmodels.enums.DmpAccessType; import org.opencdmp.commonmodels.enums.DmpBlueprintSystemFieldType; @@ -42,7 +45,7 @@ import java.util.*; @Component @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class WordFileTransformerService implements FileTransformerClient { - private final static Logger logger = LoggerFactory.getLogger(WordFileTransformerService.class); + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(WordFileTransformerService.class)); private final static List FILE_FORMATS = List.of( new FileFormat(FileFormats.PDF.getValue(), true, "fa-file-pdf-o"), @@ -75,7 +78,7 @@ public class WordFileTransformerService implements FileTransformerClient { bytes = this.pdfService.convertToPDF(bytes); yield this.getDmpFileName(dmp, ".pdf"); } - default -> throw new InvalidApplicationException("Invalid type " + fileFormat); + default -> throw new MyApplicationException("Invalid type " + fileFormat); }; FileEnvelopeModel wordFile = new FileEnvelopeModel(); @@ -99,7 +102,7 @@ public class WordFileTransformerService implements FileTransformerClient { bytes = this.pdfService.convertToPDF(bytes); yield this.getDescriptionFileName(descriptionModel, ".pdf"); } - default -> throw new InvalidApplicationException("Invalid type " + fileFormat); + default -> throw new MyApplicationException("Invalid type " + fileFormat); }; FileEnvelopeModel wordFile = new FileEnvelopeModel(); @@ -116,12 +119,12 @@ public class WordFileTransformerService implements FileTransformerClient { @Override public DmpModel importDmp(FileEnvelopeModel envelope) { - throw new UnsupportedOperationException("import not supported"); + throw new MyApplicationException("import not supported"); } @Override public DescriptionModel importDescription(FileEnvelopeModel envelope) { - throw new UnsupportedOperationException("import not supported"); + throw new MyApplicationException("import not supported"); } @Override @@ -149,11 +152,11 @@ public class WordFileTransformerService implements FileTransformerClient { private byte[] buildDmpWordDocument(DmpModel dmpEntity) throws IOException, InvalidApplicationException { - if (dmpEntity == null) throw new IllegalArgumentException("DmpEntity required"); + if (dmpEntity == null) throw new MyApplicationException("DmpEntity required"); DmpBlueprintModel dmpBlueprintModel = dmpEntity.getDmpBlueprint(); - if (dmpBlueprintModel == null) throw new IllegalArgumentException("DmpBlueprint required"); - if (dmpBlueprintModel.getDefinition() == null) throw new IllegalArgumentException("DmpBlueprint Definition required"); - if (dmpBlueprintModel.getDefinition().getSections() == null) throw new IllegalArgumentException("DmpBlueprint Section required"); + if (dmpBlueprintModel == null) throw new MyApplicationException("DmpBlueprint required"); + if (dmpBlueprintModel.getDefinition() == null) throw new MyApplicationException("DmpBlueprint Definition required"); + if (dmpBlueprintModel.getDefinition().getSections() == null) throw new MyApplicationException("DmpBlueprint Section required"); XWPFDocument document = new XWPFDocument(new FileInputStream(ResourceUtils.getFile(this.wordFileTransformerServiceProperties.getWordDmpTemplate()))); @@ -218,8 +221,8 @@ public class WordFileTransformerService implements FileTransformerClient { } private void buildSectionDescriptions(XWPFDocument document, List descriptions) { - if (document == null) throw new IllegalArgumentException("Document required"); - if (descriptions == null) throw new IllegalArgumentException("Descriptions required"); + if (document == null) throw new MyApplicationException("Document required"); + if (descriptions == null) throw new MyApplicationException("Descriptions required"); List descriptionTemplateModels = descriptions.stream().map(DescriptionModel::getDescriptionTemplate).toList(); if (descriptionTemplateModels.isEmpty()) return; @@ -238,8 +241,8 @@ public class WordFileTransformerService implements FileTransformerClient { } private void buildSectionDescription(XWPFDocument document, DescriptionModel descriptionModel) { - if (document == null) throw new IllegalArgumentException("Document required"); - if (descriptionModel == null) throw new IllegalArgumentException("DescriptionModel required"); + if (document == null) throw new MyApplicationException("Document required"); + if (descriptionModel == null) throw new MyApplicationException("DescriptionModel required"); DescriptionTemplateModel descriptionTemplateModelFileModel = descriptionModel.getDescriptionTemplate(); @@ -276,7 +279,7 @@ public class WordFileTransformerService implements FileTransformerClient { try { this.wordBuilder.build(document, descriptionModel.getDescriptionTemplate(), descriptionModel.getProperties(), new VisibilityServiceImpl(descriptionModel.getVisibilityStates())); - } catch (IOException e) { + } catch (Exception e) { logger.error(e.getMessage(), e); } // Page break at the end of the Dataset. @@ -286,8 +289,8 @@ public class WordFileTransformerService implements FileTransformerClient { private void buildDmpSectionField(DmpModel dmpEntity, XWPFDocument document, FieldModel fieldModel) throws InvalidApplicationException { - if (fieldModel == null) throw new IllegalArgumentException("Field required"); - if (fieldModel.getCategory() == null) throw new IllegalArgumentException("Field is required" + fieldModel.getId() + " " + fieldModel.getLabel()); + if (fieldModel == null) throw new MyApplicationException("Field required"); + if (fieldModel.getCategory() == null) throw new MyApplicationException("Field is required" + fieldModel.getId() + " " + fieldModel.getLabel()); switch (fieldModel.getCategory()){ case System -> { buildDmpSectionSystemField(dmpEntity, document, (SystemFieldModel) fieldModel); @@ -296,15 +299,15 @@ public class WordFileTransformerService implements FileTransformerClient { case ReferenceType -> { buildDmpSectionReferenceTypeField(dmpEntity, document, (ReferenceTypeFieldModel) fieldModel); } - default -> throw new InvalidApplicationException("Invalid type " + fieldModel.getCategory()); + default -> throw new MyApplicationException("Invalid type " + fieldModel.getCategory()); } } private void buildDmpSectionReferenceTypeField(DmpModel dmpEntity, XWPFDocument document, ReferenceTypeFieldModel referenceField) { - if (referenceField == null) throw new IllegalArgumentException("ReferenceField required"); - if (dmpEntity == null) throw new IllegalArgumentException("DmpEntity required"); - if (document == null) throw new IllegalArgumentException("Document required"); - if (referenceField.getReferenceType() == null) throw new IllegalArgumentException("ReferenceField type required"); + if (referenceField == null) throw new MyApplicationException("ReferenceField required"); + if (dmpEntity == null) throw new MyApplicationException("DmpEntity required"); + if (document == null) throw new MyApplicationException("Document required"); + if (referenceField.getReferenceType() == null) throw new MyApplicationException("ReferenceField type required"); if (referenceField.getReferenceType().getCode() == null && !referenceField.getReferenceType().getCode().isBlank()) throw new IllegalArgumentException("ReferenceField type code required"); XWPFParagraph systemFieldParagraph = document.createParagraph(); @@ -325,9 +328,9 @@ public class WordFileTransformerService implements FileTransformerClient { } private void buildDmpSectionSystemField(DmpModel dmpEntity, XWPFDocument document, SystemFieldModel systemField) throws InvalidApplicationException { - if (systemField == null) throw new IllegalArgumentException("SystemField required"); - if (dmpEntity == null) throw new IllegalArgumentException("DmpEntity required"); - if (document == null) throw new IllegalArgumentException("Document required"); + if (systemField == null) throw new MyApplicationException("SystemField required"); + if (dmpEntity == null) throw new MyApplicationException("DmpEntity required"); + if (document == null) throw new MyApplicationException("Document required"); if (DmpBlueprintSystemFieldType.Language.equals(systemField.getSystemFieldType()) || DmpBlueprintSystemFieldType.User.equals(systemField.getSystemFieldType())) return; @@ -364,12 +367,12 @@ public class WordFileTransformerService implements FileTransformerClient { case Language: break; default: - throw new InvalidApplicationException("Invalid type " + systemField.getSystemFieldType()); + throw new MyApplicationException("Invalid type " + systemField.getSystemFieldType()); } } private void buildDmpSectionExtraField(DmpModel dmpEntity, XWPFDocument document, ExtraFieldModel extraFieldModel) throws InvalidApplicationException { - if (extraFieldModel == null) throw new IllegalArgumentException("ExtraFieldModel required"); + if (extraFieldModel == null) throw new MyApplicationException("ExtraFieldModel required"); XWPFParagraph extraFieldParagraph = document.createParagraph(); extraFieldParagraph.setSpacingBetween(1.0); XWPFRun runExtraFieldLabel = extraFieldParagraph.createRun(); @@ -390,13 +393,13 @@ public class WordFileTransformerService implements FileTransformerClient { runExtraFieldInput.setColor("116a78"); break; default: - throw new InvalidApplicationException("Invalid type " + extraFieldModel.getDataType()); + throw new MyApplicationException("Invalid type " + extraFieldModel.getDataType()); } } } private String getDmpFileName(DmpModel dmpModel, String extension){ - if (dmpModel == null) throw new IllegalArgumentException("DmpEntity required"); + if (dmpModel == null) throw new MyApplicationException("DmpEntity required"); List grants = this.getReferenceModelOfTypeCode(dmpModel, this.wordFileTransformerServiceProperties.getGrantReferenceCode(), null); String fileName; @@ -411,9 +414,9 @@ public class WordFileTransformerService implements FileTransformerClient { } private byte[] buildDescriptionWordDocument(DescriptionModel descriptionModel) throws IOException { - if (descriptionModel == null) throw new IllegalArgumentException("DmpEntity required"); + if (descriptionModel == null) throw new MyApplicationException("DmpEntity required"); DmpModel dmpEntity = descriptionModel.getDmp(); - if (dmpEntity == null) throw new IllegalArgumentException("Dmp is invalid"); + if (dmpEntity == null) throw new MyApplicationException("Dmp is invalid"); XWPFDocument document = new XWPFDocument(new FileInputStream(ResourceUtils.getFile(this.wordFileTransformerServiceProperties.getWordDescriptionTemplate()))); this.wordBuilder.fillFirstPage(dmpEntity, descriptionModel, document, true); @@ -450,7 +453,7 @@ public class WordFileTransformerService implements FileTransformerClient { } private String getDescriptionFileName(DescriptionModel descriptionModel, String extension){ - if (descriptionModel == null) throw new IllegalArgumentException("DmpEntity required"); + if (descriptionModel == null) throw new MyApplicationException("DmpEntity required"); String fileName = descriptionModel.getLabel().replaceAll("[^a-zA-Z0-9+ ]", ""); return fileName + extension; diff --git a/core/src/main/java/org/opencdmp/filetransformer/docx/service/wordfiletransformer/word/WordBuilderImpl.java b/core/src/main/java/org/opencdmp/filetransformer/docx/service/wordfiletransformer/word/WordBuilderImpl.java index 5f18afd..267e156 100644 --- a/core/src/main/java/org/opencdmp/filetransformer/docx/service/wordfiletransformer/word/WordBuilderImpl.java +++ b/core/src/main/java/org/opencdmp/filetransformer/docx/service/wordfiletransformer/word/WordBuilderImpl.java @@ -1,5 +1,6 @@ package org.opencdmp.filetransformer.docx.service.wordfiletransformer.word; +import gr.cite.tools.exception.MyApplicationException; import org.opencdmp.commonmodels.enums.FieldType; import org.opencdmp.commonmodels.models.FileEnvelopeModel; import org.opencdmp.commonmodels.models.description.DescriptionModel; @@ -273,6 +274,7 @@ public class WordBuilderImpl implements WordBuilder { if (object instanceof BigInteger) return ((BigInteger) object).intValue(); return (int) object; } catch (Exception e){ + logger.error(e.getMessage(), e); return 0; } } @@ -465,50 +467,62 @@ public class WordBuilderImpl implements WordBuilder { for (FieldModel field : tempFields) { if (field.getIncludeInExport() && visibilityService.isVisible(field.getId(), propertyDefinitionFieldSetItemModel.getOrdinal())) { if (!createListing) { - try { - org.opencdmp.commonmodels.models.description.FieldModel fieldValueModel = propertyDefinitionFieldSetItemModel.getFields().getOrDefault(field.getId(), null); - if (field.getData().getFieldType().equals(FieldType.UPLOAD)) { - boolean isImage = false; - for (UploadDataModel.UploadOptionModel type : ((UploadDataModel) field.getData()).getTypes()) { - String fileFormat = type.getValue(); - if (IMAGE_TYPE_MAP.containsKey(fileFormat)) { - isImage = true; - break; + org.opencdmp.commonmodels.models.description.FieldModel fieldValueModel = propertyDefinitionFieldSetItemModel.getFields().getOrDefault(field.getId(), null); + if (field.getData().getFieldType().equals(FieldType.UPLOAD)) { + boolean isImage = false; + for (UploadDataModel.UploadOptionModel type : ((UploadDataModel) field.getData()).getTypes()) { + String fileFormat = type.getValue(); + if (IMAGE_TYPE_MAP.containsKey(fileFormat)) { + isImage = true; + break; + } + } + if (isImage) { + if (fieldValueModel != null && fieldValueModel.getTextValue() != null && !fieldValueModel.getTextValue().isEmpty()) { + XWPFParagraph paragraph = addCellContent(fieldValueModel.getFile(), mainDocumentPart, ParagraphStyle.IMAGE, numId, 0, numOfRows, numOfCells, 0); + if (paragraph != null) { + hasValue = true; + } + if (hasMultiplicityItems) { + hasMultiplicityItems = false; } } - if (isImage) { - if (fieldValueModel != null && fieldValueModel.getTextValue() != null && !fieldValueModel.getTextValue().isEmpty()) { - XWPFParagraph paragraph = addCellContent(fieldValueModel.getFile(), mainDocumentPart, ParagraphStyle.IMAGE, numId, 0, numOfRows, numOfCells, 0); - if (paragraph != null) { - hasValue = true; - } - if (hasMultiplicityItems) { - hasMultiplicityItems = false; - } - } - } - } else if (fieldValueModel != null) { - this.indent = indent; - boolean isResearcher = false; - if (field.getData() instanceof ReferenceTypeDataModel) { - isResearcher = ((ReferenceTypeDataModel) field.getData()).getReferenceType().getCode().equals(this.wordFileTransformerServiceProperties.getResearcherReferenceCode()); - } + } + } else if (fieldValueModel != null) { + this.indent = indent; + boolean isResearcher = false; + if (field.getData() instanceof ReferenceTypeDataModel) { + isResearcher = ((ReferenceTypeDataModel) field.getData()).getReferenceType().getCode().equals(this.wordFileTransformerServiceProperties.getResearcherReferenceCode()); + } - List extractValues = this.extractValues(field, fieldValueModel); - if (!extractValues.isEmpty()){ - int numOfValuesInCell = 0; - for (String extractValue : extractValues){ - boolean orcidResearcher = false; - String orcId = null; - if (isResearcher && extractValue.contains("orcid:")) { - orcId = extractValue.substring(extractValue.indexOf(':') + 1, extractValue.indexOf(')')); - extractValue = extractValue.substring(0, extractValue.indexOf(':') + 1) + " "; - orcidResearcher = true; + List extractValues = this.extractValues(field, fieldValueModel); + if (!extractValues.isEmpty()){ + int numOfValuesInCell = 0; + for (String extractValue : extractValues){ + boolean orcidResearcher = false; + String orcId = null; + if (isResearcher && extractValue.contains("orcid:")) { + orcId = extractValue.substring(extractValue.indexOf(':') + 1, extractValue.indexOf(')')); + extractValue = extractValue.substring(0, extractValue.indexOf(':') + 1) + " "; + orcidResearcher = true; + } + if (extractValues.size() > 1) extractValue = "• " + extractValue; + if (hasMultiplicityItems) { + XWPFParagraph paragraph = mainDocumentPart.getCell(mainDocumentPart.getTableCells().size()).addParagraph(); + paragraph.createRun().setText(extractValue); + if (orcidResearcher) { + XWPFHyperlinkRun run = paragraph.createHyperlinkRun("https://orcid.org/" + orcId); + run.setText(orcId); + run.setUnderline(UnderlinePatterns.SINGLE); + run.setColor("0000FF"); + paragraph.createRun().setText(")"); } - if (extractValues.size() > 1) extractValue = "• " + extractValue; - if (hasMultiplicityItems) { - XWPFParagraph paragraph = mainDocumentPart.getCell(mainDocumentPart.getTableCells().size()).addParagraph(); - paragraph.createRun().setText(extractValue); + hasValue = true; + hasMultiplicityItems = false; + } else { + XWPFParagraph paragraph = addCellContent(extractValue, mainDocumentPart, field.getData().getFieldType().equals(FieldType.RICH_TEXT_AREA) ? ParagraphStyle.HTML : ParagraphStyle.TEXT, numId, indent, numOfRows, numOfCells, numOfValuesInCell); + if (paragraph != null) { + numOfValuesInCell++; if (orcidResearcher) { XWPFHyperlinkRun run = paragraph.createHyperlinkRun("https://orcid.org/" + orcId); run.setText(orcId); @@ -517,26 +531,10 @@ public class WordBuilderImpl implements WordBuilder { paragraph.createRun().setText(")"); } hasValue = true; - hasMultiplicityItems = false; - } else { - XWPFParagraph paragraph = addCellContent(extractValue, mainDocumentPart, field.getData().getFieldType().equals(FieldType.RICH_TEXT_AREA) ? ParagraphStyle.HTML : ParagraphStyle.TEXT, numId, indent, numOfRows, numOfCells, numOfValuesInCell); - if (paragraph != null) { - numOfValuesInCell++; - if (orcidResearcher) { - XWPFHyperlinkRun run = paragraph.createHyperlinkRun("https://orcid.org/" + orcId); - run.setText(orcId); - run.setUnderline(UnderlinePatterns.SINGLE); - run.setColor("0000FF"); - paragraph.createRun().setText(")"); - } - hasValue = true; - } } } } } - } catch (InvalidApplicationException e) { - logger.error(e.getMessage(), e); } } numOfCells++; @@ -581,7 +579,6 @@ public class WordBuilderImpl implements WordBuilder { for (FieldModel field : tempFields) { if (field.getIncludeInExport() && visibilityService.isVisible(field.getId(), propertyDefinitionFieldSetItemModel.getOrdinal())) { if (!createListing) { - try { org.opencdmp.commonmodels.models.description.FieldModel fieldValueModel = propertyDefinitionFieldSetItemModel.getFields().getOrDefault(field.getId(), null); if (field.getData() != null) { if (field.getData().getFieldType().equals(FieldType.UPLOAD)) { @@ -684,9 +681,6 @@ public class WordBuilderImpl implements WordBuilder { } } } - } catch (InvalidApplicationException e) { - throw new RuntimeException(e); - } } } } @@ -757,7 +751,7 @@ public class WordBuilderImpl implements WordBuilder { } } - private List extractValues(FieldModel field, org.opencdmp.commonmodels.models.description.FieldModel fieldValueModel) throws InvalidApplicationException { + private List extractValues(FieldModel field, org.opencdmp.commonmodels.models.description.FieldModel fieldValueModel) { List values = new ArrayList<>(); if (fieldValueModel == null || field == null || field.getData() == null) { return values; @@ -834,7 +828,7 @@ public class WordBuilderImpl implements WordBuilder { case INTERNAL_ENTRIES_DMPS: break; default: - throw new InvalidApplicationException("Invalid type " + field.getData().getFieldType()); + throw new MyApplicationException("Invalid type " + field.getData().getFieldType()); } return values; @@ -842,7 +836,7 @@ public class WordBuilderImpl implements WordBuilder { @Override public int findPosOfPoweredBy(XWPFDocument document) { - if (document == null) throw new IllegalArgumentException("Document required"); + if (document == null) throw new MyApplicationException("Document required"); if (document.getParagraphs() == null) return -1; for (XWPFParagraph p : document.getParagraphs()) { @@ -874,8 +868,8 @@ public class WordBuilderImpl implements WordBuilder { @Override public void fillFirstPage(DmpModel dmpEntity, DescriptionModel descriptionModel, XWPFDocument document, boolean isDescription) { - if (dmpEntity == null) throw new IllegalArgumentException("DmpEntity required"); - if (document == null) throw new IllegalArgumentException("Document required"); + if (dmpEntity == null) throw new MyApplicationException("DmpEntity required"); + if (document == null) throw new MyApplicationException("Document required"); int parPos = 0; int descrParPos = -1; @@ -1082,7 +1076,7 @@ public class WordBuilderImpl implements WordBuilder { @Override public void fillFooter(DmpModel dmpEntity, DescriptionModel descriptionModel, XWPFDocument document) { - if (dmpEntity == null) throw new IllegalArgumentException("DmpEntity required"); + if (dmpEntity == null) throw new MyApplicationException("DmpEntity required"); List licences = this.getReferenceModelOfTypeCode(dmpEntity, this.wordFileTransformerServiceProperties.getLicenceReferenceCode()); document.getFooterList().forEach(xwpfFooter -> { diff --git a/pom.xml b/pom.xml index 224a5a2..d63f4f0 100644 --- a/pom.xml +++ b/pom.xml @@ -37,6 +37,11 @@ commons-compress 1.26.1 + + gr.cite + logging + 2.2.0 + diff --git a/web/pom.xml b/web/pom.xml index 6c89d89..4d55b90 100644 --- a/web/pom.xml +++ b/web/pom.xml @@ -44,7 +44,12 @@ org.springframework.boot spring-boot-starter-cache - + + gr.cite + exceptions + 2.2.0 + + diff --git a/web/src/main/java/org/opencdmp/filetransformer/docx/web/controller/FileTransformerController.java b/web/src/main/java/org/opencdmp/filetransformer/docx/web/controller/FileTransformerController.java index 5e488eb..e6f3724 100644 --- a/web/src/main/java/org/opencdmp/filetransformer/docx/web/controller/FileTransformerController.java +++ b/web/src/main/java/org/opencdmp/filetransformer/docx/web/controller/FileTransformerController.java @@ -1,42 +1,91 @@ package org.opencdmp.filetransformer.docx.web.controller; +import gr.cite.tools.auditing.AuditService; +import gr.cite.tools.logging.LoggerService; +import gr.cite.tools.logging.MapLogEntry; import org.opencdmp.commonmodels.models.FileEnvelopeModel; import org.opencdmp.commonmodels.models.description.DescriptionModel; import org.opencdmp.commonmodels.models.dmp.DmpModel; +import org.opencdmp.filetransformer.docx.audit.AuditableAction; import org.opencdmp.filetransformerbase.interfaces.FileTransformerClient; import org.opencdmp.filetransformerbase.interfaces.FileTransformerConfiguration; import org.opencdmp.filetransformer.docx.service.wordfiletransformer.WordFileTransformerService; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; +import java.util.AbstractMap; +import java.util.Map; + @RestController @RequestMapping("/api/file-transformer") public class FileTransformerController implements org.opencdmp.filetransformerbase.interfaces.FileTransformerController { + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(FileTransformerController.class)); private final FileTransformerClient fileTransformerExecutor; + private final AuditService auditService; + @Autowired - public FileTransformerController(WordFileTransformerService fileTransformerExecutor) { + public FileTransformerController(FileTransformerClient fileTransformerExecutor, AuditService auditService) { this.fileTransformerExecutor = fileTransformerExecutor; + this.auditService = auditService; } public FileEnvelopeModel exportDmp(@RequestBody DmpModel dmpDepositModel, @RequestParam(value = "format",required = false)String format) throws Exception { - return fileTransformerExecutor.exportDmp(dmpDepositModel, format); + logger.debug(new MapLogEntry("exportDmp " + DmpModel.class.getSimpleName()).And("dmpDepositModel", dmpDepositModel).And("format", format)); + + FileEnvelopeModel model = fileTransformerExecutor.exportDmp(dmpDepositModel, format); + + this.auditService.track(AuditableAction.FileTransformer_ExportDmp, Map.ofEntries( + new AbstractMap.SimpleEntry("dmpDepositModel", dmpDepositModel), + new AbstractMap.SimpleEntry("format", format) + )); + return model; } public FileEnvelopeModel exportDescription(@RequestBody DescriptionModel descriptionModel, @RequestParam(value = "format",required = false)String format) throws Exception { - return fileTransformerExecutor.exportDescription(descriptionModel, format); + logger.debug(new MapLogEntry("exportDescription " + DescriptionModel.class.getSimpleName()).And("descriptionModel", descriptionModel).And("format", format)); + + FileEnvelopeModel model = fileTransformerExecutor.exportDescription(descriptionModel, format); + + this.auditService.track(AuditableAction.FileTransformer_ExportDescription, Map.ofEntries( + new AbstractMap.SimpleEntry("descriptionModel", descriptionModel), + new AbstractMap.SimpleEntry("format", format) + )); + return model; } public DmpModel importFileToDmp(@RequestBody FileEnvelopeModel fileEnvelope) { - return fileTransformerExecutor.importDmp(fileEnvelope); + + logger.debug(new MapLogEntry("importFileToDmp " + FileEnvelopeModel.class.getSimpleName()).And("fileEnvelope", fileEnvelope)); + + DmpModel model = fileTransformerExecutor.importDmp(fileEnvelope); + + this.auditService.track(AuditableAction.FileTransformer_ImportFileToDmp, Map.ofEntries( + new AbstractMap.SimpleEntry("fileEnvelope", fileEnvelope) + )); + return model; } public DescriptionModel importFileToDescription(@RequestBody FileEnvelopeModel fileEnvelope) { - return fileTransformerExecutor.importDescription(fileEnvelope); + logger.debug(new MapLogEntry("importFileToDescription " + FileEnvelopeModel.class.getSimpleName()).And("fileEnvelope", fileEnvelope)); + + DescriptionModel model = fileTransformerExecutor.importDescription(fileEnvelope); + + this.auditService.track(AuditableAction.FileTransformer_ImportFileToDescription, Map.ofEntries( + new AbstractMap.SimpleEntry("importFileToDescription ", fileEnvelope) + )); + return model; } public FileTransformerConfiguration getSupportedFormats() { - return fileTransformerExecutor.getConfiguration(); + logger.debug(new MapLogEntry("getSupportedFormats")); + + FileTransformerConfiguration model = fileTransformerExecutor.getConfiguration(); + + this.auditService.track(AuditableAction.FileTransformer_GetSupportedFormats); + + return model; } } diff --git a/web/src/main/java/org/opencdmp/filetransformer/docx/web/controller/controllerhandler/GlobalExceptionHandler.java b/web/src/main/java/org/opencdmp/filetransformer/docx/web/controller/controllerhandler/GlobalExceptionHandler.java new file mode 100644 index 0000000..1441e2f --- /dev/null +++ b/web/src/main/java/org/opencdmp/filetransformer/docx/web/controller/controllerhandler/GlobalExceptionHandler.java @@ -0,0 +1,202 @@ +package org.opencdmp.filetransformer.docx.web.controller.controllerhandler; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import gr.cite.tools.exception.*; +import gr.cite.tools.logging.LoggerService; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.context.request.WebRequest; + +import java.text.MessageFormat; +import java.util.HashMap; +import java.util.Map; + +@RestControllerAdvice +@ControllerAdvice +public class GlobalExceptionHandler { + + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(GlobalExceptionHandler.class)); + + private final ObjectMapper objectMapper; + + public GlobalExceptionHandler() { + this.objectMapper = new ObjectMapper(); + this.objectMapper.registerModule(new JavaTimeModule()); + } + + + @ExceptionHandler(Exception.class) + public ResponseEntity handleUnexpectedErrors(Exception exception, WebRequest request) throws Exception { + HandledException handled = this.handleException(exception, request); + this.log(handled.getLevel(), exception, MessageFormat.format("returning code {0} and payload {1}", handled.getStatusCode(), handled.getMessage())); + return new ResponseEntity<>(handled.getMessage(), handled.getStatusCode()); + } + + public String toJsonSafe(Object item) { + if (item == null) return null; + try { + return this.objectMapper.writeValueAsString(item); + } catch (Exception ex) { + return null; + } + } + + public void log(System.Logger.Level level, Exception e, String message) { + if (level != null) { + switch (level) { + case TRACE: + logger.trace(message, e); + break; + case DEBUG: + logger.debug(message, e); + break; + case INFO: + logger.info(message, e); + break; + case WARNING: + logger.warn(message, e); + break; + case ERROR: + logger.error(message, e); + break; + default: + logger.error(e); + } + } else { + logger.error(e); + } + } + + public HandledException handleException(Exception exception, WebRequest request) throws Exception { + HttpStatus statusCode; + Map result; + System.Logger.Level logLevel; + + switch (exception){ + case MyNotFoundException myNotFoundException -> { + logLevel = System.Logger.Level.DEBUG; + statusCode = HttpStatus.NOT_FOUND; + int code = myNotFoundException.getCode(); + if (code > 0) { + result = Map.ofEntries( + Map.entry("code", code), + Map.entry("error", myNotFoundException.getMessage()) + ); + } + else { + result = Map.ofEntries( + Map.entry("error", myNotFoundException.getMessage()) + ); + } + } + case MyUnauthorizedException myUnauthorizedException -> { + logLevel = System.Logger.Level.DEBUG; + statusCode = HttpStatus.UNAUTHORIZED; + int code = myUnauthorizedException.getCode(); + if (code > 0) { + result = Map.ofEntries( + Map.entry("code", code), + Map.entry("error", myUnauthorizedException.getMessage()) + ); + } + else { + result = Map.ofEntries( + Map.entry("error", myUnauthorizedException.getMessage()) + ); + } + } + case MyForbiddenException myForbiddenException -> { + logLevel = System.Logger.Level.DEBUG; + statusCode = HttpStatus.FORBIDDEN; + int code = myForbiddenException.getCode(); + if (code > 0) { + result = Map.ofEntries( + Map.entry("code", code), + Map.entry("error", myForbiddenException.getMessage()) + ); + } + else { + result = Map.ofEntries( + Map.entry("error", myForbiddenException.getMessage()) + ); + } + } + case MyValidationException myValidationException -> { + logLevel = System.Logger.Level.DEBUG; + statusCode = HttpStatus.BAD_REQUEST; + int code = myValidationException.getCode(); + + result = new HashMap<>(); + if (code > 0) result.put("code", code); + if (myValidationException.getMessage() != null) result.put("error", myValidationException.getMessage()); + if (myValidationException.getErrors() != null) result.put("message", myValidationException.getErrors()); + } + case MyApplicationException myApplicationException -> { + logLevel = System.Logger.Level.ERROR; + statusCode = HttpStatus.INTERNAL_SERVER_ERROR; + int code = myApplicationException.getCode(); + if (code > 0) { + result = Map.ofEntries( + Map.entry("code", code), + Map.entry("error", myApplicationException.getMessage()) + ); + } + else { + result = Map.ofEntries( + Map.entry("error", myApplicationException.getMessage()) + ); + } + } + default -> { + logLevel = System.Logger.Level.ERROR; + statusCode = HttpStatus.INTERNAL_SERVER_ERROR; + result = Map.ofEntries( + Map.entry("error", "System error") + ); + } + }; + String serialization = this.toJsonSafe(result); + return new HandledException(statusCode, serialization, logLevel); + } + + public static class HandledException{ + public HttpStatus statusCode; + public String message; + public System.Logger.Level level; + + public HandledException(HttpStatus statusCode, String message, System.Logger.Level level) { + this.statusCode = statusCode; + this.message = message; + this.level = level; + } + + public HttpStatus getStatusCode() { + return statusCode; + } + + public void setStatusCode(HttpStatus statusCode) { + this.statusCode = statusCode; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public System.Logger.Level getLevel() { + return level; + } + + public void setLevel(System.Logger.Level level) { + this.level = level; + } + } +} diff --git a/web/src/main/resources/config/application.yml b/web/src/main/resources/config/application.yml index d7e494a..bbbe5fd 100644 --- a/web/src/main/resources/config/application.yml +++ b/web/src/main/resources/config/application.yml @@ -7,6 +7,8 @@ spring: optional:classpath:config/storage.yml[.yml], optional:classpath:config/storage-${spring.profiles.active}.yml[.yml], optional:file:../config/storage-${spring.profiles.active}.yml[.yml], optional:classpath:config/security.yml[.yml], optional:classpath:config/security-${spring.profiles.active}.yml[.yml], optional:file:../config/security-${spring.profiles.active}.yml[.yml], optional:classpath:config/cache.yml[.yml], optional:classpath:config/cache-${spring.profiles.active}.yml[.yml], optional:file:../config/cache-${spring.profiles.active}.yml[.yml], + optional:classpath:config/logging.yml[.yml], optional:classpath:config/logging-${spring.profiles.active}.yml[.yml], optional:file:../config/logging-${spring.profiles.active}.yml[.yml], optional:classpath:config/word-file-transformer.yml[.yml], optional:classpath:config/word-file-transformer-${spring.profiles.active}.yml[.yml], optional:file:../config/word-file-transformer-${spring.profiles.active}.yml[.yml], - optional:classpath:config/pdf.yml[.yml], optional:classpath:config/pdf-${spring.profiles.active}.yml[.yml], optional:file:../config/pdf-${spring.profiles.active}.yml[.yml] + optional:classpath:config/pdf.yml[.yml], optional:classpath:config/pdf-${spring.profiles.active}.yml[.yml], optional:file:../config/pdf-${spring.profiles.active}.yml[.yml], + optional:classpath:config/idpclaims.yml[.yml], optional:classpath:config/idpclaims-${spring.profiles.active}.yml[.yml], optional:file:../config/idpclaims-${spring.profiles.active}.yml[.yml] diff --git a/web/src/main/resources/config/idpclaims.yml b/web/src/main/resources/config/idpclaims.yml new file mode 100644 index 0000000..53bc069 --- /dev/null +++ b/web/src/main/resources/config/idpclaims.yml @@ -0,0 +1,41 @@ +idpclient: + claims: + mapping: + Subject: + - type: sub + Name: + - type: name + Client: + - type: client_id + AuthenticationMethod: + - type: amr + NotBefore: + - type: nbf + AuthenticatedAt: + - type: auth_time + ExpiresAt: + - type: exp + Email: + - type: email + Roles: + - type: resource_access + path: dmp_zenodo_bridge.roles + Scope: + - type: scope + AccessToken: + - type: x-access-token + visibility: SENSITIVE + IssuedAt: + - type: iat + Issuer: + - type: iss + Audience: + - type: aud + TokenType: + - type: typ + AuthorizedParty: + - type: azp + Authorities: + - type: authorities + ExternalProviderName: + - type: identity_provider \ No newline at end of file diff --git a/web/src/main/resources/config/logging-devel.yml b/web/src/main/resources/config/logging-devel.yml new file mode 100644 index 0000000..59f65e4 --- /dev/null +++ b/web/src/main/resources/config/logging-devel.yml @@ -0,0 +1,36 @@ +logging: + config: classpath:logging/logback-${spring.profiles.active}.xml + context: + request: + requestIdKey: req.id + requestRemoteHostKey: req.remoteHost + requestUriKey: req.requestURI + requestQueryStringKey: req.queryString + requestUrlKey : req.requestURL + requestMethodKey: req.method + requestUserAgentKey: req.userAgent + requestForwardedForKey: req.xForwardedFor + requestSchemeKey: req.scheme + requestRemoteAddressKey: req.remoteAddr + requestRemotePortKey: req.remotePort + requestRemoteUserKey: req.remoteUser + principal: + subjectKey: usr.subject + nameKey: usr.name + clientKey: usr.client +audit: + enable: true + requestRemoteHostKey: req.remoteHost + requestUriKey: req.requestURI + requestQueryStringKey: req.queryString + requestUrlKey : req.requestURL + requestMethodKey: req.method + requestUserAgentKey: req.userAgent + requestForwardedForKey: req.xForwardedFor + requestSchemeKey: req.scheme + requestRemoteAddressKey: req.remoteAddr + requestRemotePortKey: req.remotePort + requestRemoteUserKey: req.remoteUser + principalSubjectKey: usr.subject + principalNameKey: usr.name + principalClientKey: usr.client diff --git a/web/src/main/resources/config/logging.yml b/web/src/main/resources/config/logging.yml new file mode 100644 index 0000000..56e152d --- /dev/null +++ b/web/src/main/resources/config/logging.yml @@ -0,0 +1,35 @@ +logging: + context: + request: + requestIdKey: req.id + requestRemoteHostKey: req.remoteHost + requestUriKey: req.requestURI + requestQueryStringKey: req.queryString + requestUrlKey : req.requestURL + requestMethodKey: req.method + requestUserAgentKey: req.userAgent + requestForwardedForKey: req.xForwardedFor + requestSchemeKey: req.scheme + requestRemoteAddressKey: req.remoteAddr + requestRemotePortKey: req.remotePort + requestRemoteUserKey: req.remoteUser + principal: + subjectKey: usr.subject + nameKey: usr.name + clientKey: usr.client +audit: + enable: true + requestRemoteHostKey: req.remoteHost + requestUriKey: req.requestURI + requestQueryStringKey: req.queryString + requestUrlKey : req.requestURL + requestMethodKey: req.method + requestUserAgentKey: req.userAgent + requestForwardedForKey: req.xForwardedFor + requestSchemeKey: req.scheme + requestRemoteAddressKey: req.remoteAddr + requestRemotePortKey: req.remotePort + requestRemoteUserKey: req.remoteUser + principalSubjectKey: usr.subject + principalNameKey: usr.name + principalClientKey: usr.client diff --git a/web/src/main/resources/logging/logback-devel.xml b/web/src/main/resources/logging/logback-devel.xml new file mode 100644 index 0000000..b4d541f --- /dev/null +++ b/web/src/main/resources/logging/logback-devel.xml @@ -0,0 +1,62 @@ + + + + + %date{ISO8601} [%thread] %-5level %logger{36} [%X{req.id}] - %message%n + + + + + logs/logging.log + + logs/logging.%d{yyyy-MM-dd}.%i.log + + 100MB + + 15 + + + %date{ISO8601} [%thread] %-5level %logger{36} [%X{req.id}] - %message%n + + + + + logs/auditing.log + + logs/auditing.%d{yyyy-MM-dd}.%i.log + + 100MB + + 15 + + + %date{ISO8601} - %X{req.id} - %message%n + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file