From 796f02f6a5d5e3123e3bacc6f2e466f146284c36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20N=C3=B6hles?= Date: Thu, 30 Jan 2025 12:24:08 +0100 Subject: [PATCH 1/7] Add Validator Chain --- .../easybill/Dtos/ValidationResult.java | 56 +++---------- .../github/easybill/Dtos/ValidatorResult.java | 55 +++++++++++++ .../Exceptions/ValidationChainException.java | 8 ++ .../GlobalExceptionInterceptor.java | 12 +++ .../Services/SchematronValidationService.java | 3 +- .../FacturXSchematronValidator.java | 48 ++++++++--- .../Validators/PeppolSchematronValidator.java | 48 ++++++++--- .../XRechnungSchematronValidator.java | 67 +++++++++++++--- .../resources/EN16931/EN16931_1.3.13_CII.sch | 80 +++++++++---------- 9 files changed, 254 insertions(+), 123 deletions(-) create mode 100644 src/main/java/io/github/easybill/Dtos/ValidatorResult.java create mode 100644 src/main/java/io/github/easybill/Exceptions/ValidationChainException.java diff --git a/src/main/java/io/github/easybill/Dtos/ValidationResult.java b/src/main/java/io/github/easybill/Dtos/ValidationResult.java index 4c75664..e06ba34 100644 --- a/src/main/java/io/github/easybill/Dtos/ValidationResult.java +++ b/src/main/java/io/github/easybill/Dtos/ValidationResult.java @@ -1,71 +1,39 @@ package io.github.easybill.Dtos; import com.fasterxml.jackson.annotation.JsonProperty; -import com.helger.schematron.svrl.jaxb.FailedAssert; import com.helger.schematron.svrl.jaxb.SchematronOutputType; import io.github.easybill.Enums.XmlProfileType; -import java.util.Collections; +import java.util.Arrays; import java.util.List; -import java.util.Objects; import org.checkerframework.checker.nullness.qual.NonNull; public record ValidationResult( @NonNull ValidationResultMetaData meta, - @NonNull List<@NonNull ValidationResultField> errors, - @NonNull List<@NonNull ValidationResultField> warnings + @JsonProperty("validation_results") + @NonNull + List<@NonNull ValidatorResult> validationResults ) { - public ValidationResult { - errors = Collections.unmodifiableList(errors); - warnings = Collections.unmodifiableList(warnings); - } - @JsonProperty("is_valid") public boolean isValid() { - return errors.isEmpty(); + return validationResults + .stream() + .allMatch(element -> element.errors().isEmpty()); } public static ValidationResult of( XmlProfileType xmlProfileType, ValidationRequest validationRequest, - SchematronOutputType schematronOutputType + SchematronOutputType... schematronOutputTypes ) { return new ValidationResult( new ValidationResultMetaData( validationRequest.xmlSyntaxType(), xmlProfileType ), - getErrorsFromSchematronOutput(schematronOutputType), - getWarningsFromSchematronOutput(schematronOutputType) + Arrays + .stream(schematronOutputTypes) + .map(ValidatorResult::of) + .toList() ); } - - private static List<@NonNull ValidationResultField> getErrorsFromSchematronOutput( - @NonNull SchematronOutputType outputType - ) { - return outputType - .getActivePatternAndFiredRuleAndFailedAssert() - .stream() - .filter(element -> element instanceof FailedAssert) - .filter(element -> - Objects.equals(((FailedAssert) element).getFlag(), "fatal") - ) - .map(element -> (FailedAssert) element) - .map(ValidationResultField::fromFailedAssert) - .toList(); - } - - private static List<@NonNull ValidationResultField> getWarningsFromSchematronOutput( - @NonNull SchematronOutputType outputType - ) { - return outputType - .getActivePatternAndFiredRuleAndFailedAssert() - .stream() - .filter(element -> element instanceof FailedAssert) - .filter(element -> - Objects.equals(((FailedAssert) element).getFlag(), "warning") - ) - .map(element -> (FailedAssert) element) - .map(ValidationResultField::fromFailedAssert) - .toList(); - } } diff --git a/src/main/java/io/github/easybill/Dtos/ValidatorResult.java b/src/main/java/io/github/easybill/Dtos/ValidatorResult.java new file mode 100644 index 0000000..d424d37 --- /dev/null +++ b/src/main/java/io/github/easybill/Dtos/ValidatorResult.java @@ -0,0 +1,55 @@ +package io.github.easybill.Dtos; + +import com.helger.schematron.svrl.jaxb.FailedAssert; +import com.helger.schematron.svrl.jaxb.SchematronOutputType; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import org.checkerframework.checker.nullness.qual.NonNull; + +public record ValidatorResult( + @NonNull List<@NonNull ValidationResultField> errors, + @NonNull List<@NonNull ValidationResultField> warnings +) { + public ValidatorResult { + errors = Collections.unmodifiableList(errors); + warnings = Collections.unmodifiableList(warnings); + } + + public static ValidatorResult of(@NonNull SchematronOutputType report) { + return new ValidatorResult( + getErrorsFromSchematronOutput(report), + getWarningsFromSchematronOutput(report) + ); + } + + private static List<@NonNull ValidationResultField> getErrorsFromSchematronOutput( + @NonNull SchematronOutputType outputType + ) { + return outputType + .getActivePatternAndFiredRuleAndFailedAssert() + .stream() + .filter(element -> element instanceof FailedAssert) + .filter(element -> + Objects.equals(((FailedAssert) element).getFlag(), "fatal") + ) + .map(element -> (FailedAssert) element) + .map(ValidationResultField::fromFailedAssert) + .toList(); + } + + private static List<@NonNull ValidationResultField> getWarningsFromSchematronOutput( + @NonNull SchematronOutputType outputType + ) { + return outputType + .getActivePatternAndFiredRuleAndFailedAssert() + .stream() + .filter(element -> element instanceof FailedAssert) + .filter(element -> + Objects.equals(((FailedAssert) element).getFlag(), "warning") + ) + .map(element -> (FailedAssert) element) + .map(ValidationResultField::fromFailedAssert) + .toList(); + } +} diff --git a/src/main/java/io/github/easybill/Exceptions/ValidationChainException.java b/src/main/java/io/github/easybill/Exceptions/ValidationChainException.java new file mode 100644 index 0000000..7bd9728 --- /dev/null +++ b/src/main/java/io/github/easybill/Exceptions/ValidationChainException.java @@ -0,0 +1,8 @@ +package io.github.easybill.Exceptions; + +public class ValidationChainException extends ValidatorException { + + public ValidationChainException() { + super(); + } +} diff --git a/src/main/java/io/github/easybill/Interceptors/GlobalExceptionInterceptor.java b/src/main/java/io/github/easybill/Interceptors/GlobalExceptionInterceptor.java index 16d6aa3..faa8996 100644 --- a/src/main/java/io/github/easybill/Interceptors/GlobalExceptionInterceptor.java +++ b/src/main/java/io/github/easybill/Interceptors/GlobalExceptionInterceptor.java @@ -5,6 +5,7 @@ import io.github.easybill.Exceptions.InvalidProfileException; import io.github.easybill.Exceptions.InvalidXmlException; import io.github.easybill.Exceptions.ParsingException; +import io.github.easybill.Exceptions.ValidationChainException; import jakarta.ws.rs.WebApplicationException; import jakarta.ws.rs.core.Response; import jakarta.ws.rs.ext.ExceptionMapper; @@ -58,6 +59,17 @@ public Response toResponse(Throwable exception) { .build(); } + if (exception instanceof ValidationChainException) { + return Response + .status(422, "Unprocessable Content") + .entity( + new ErrorMessage( + "During validation one of the applied validator failed." + ) + ) + .build(); + } + exceptionNotifier.notify(exception); logger.error("Encountered an exception:", exception); diff --git a/src/main/java/io/github/easybill/Services/SchematronValidationService.java b/src/main/java/io/github/easybill/Services/SchematronValidationService.java index fdea6c3..f5e7ad9 100644 --- a/src/main/java/io/github/easybill/Services/SchematronValidationService.java +++ b/src/main/java/io/github/easybill/Services/SchematronValidationService.java @@ -8,6 +8,7 @@ import io.github.easybill.Enums.XmlProfileType; import io.github.easybill.Exceptions.InvalidProfileException; import io.github.easybill.Exceptions.InvalidXmlException; +import io.github.easybill.Exceptions.ValidationChainException; import jakarta.enterprise.inject.Instance; import jakarta.inject.Singleton; import java.io.IOException; @@ -55,7 +56,7 @@ public SchematronValidationService( return validator .validate(validationRequest) - .orElseThrow(RuntimeException::new); + .orElseThrow(ValidationChainException::new); } throw new Exception( diff --git a/src/main/java/io/github/easybill/Services/Validators/FacturXSchematronValidator.java b/src/main/java/io/github/easybill/Services/Validators/FacturXSchematronValidator.java index 1e52fe9..215fc5b 100644 --- a/src/main/java/io/github/easybill/Services/Validators/FacturXSchematronValidator.java +++ b/src/main/java/io/github/easybill/Services/Validators/FacturXSchematronValidator.java @@ -15,15 +15,27 @@ @Singleton public final class FacturXSchematronValidator implements ISchematronValidator { - private final SchematronResourceSCH ciiSchematron; + private final SchematronResourceSCH en16931Schematron; + private final SchematronResourceSCH facturXSchematron; public FacturXSchematronValidator() { - ciiSchematron = + en16931Schematron = + new SchematronResourceSCH( + new ClassPathResource("/EN16931/EN16931_1.3.13_CII.sch") + ); + + facturXSchematron = new SchematronResourceSCH( new ClassPathResource("/FacturX/Factur-X_1.07.2_EXTENDED.sch") ); - if (!ciiSchematron.isValidSchematron()) { + if (!en16931Schematron.isValidSchematron()) { + throw new RuntimeException( + "Schematron validation for EN16931 CII failed" + ); + } + + if (!facturXSchematron.isValidSchematron()) { throw new RuntimeException( "Schematron validation for Factur-X Extended CII failed" ); @@ -32,7 +44,7 @@ public FacturXSchematronValidator() { @Override public boolean validateSchematron() { - return ciiSchematron.isValidSchematron(); + return facturXSchematron.isValidSchematron(); } @Override @@ -49,21 +61,31 @@ public Optional validate( ValidationRequest validationRequest ) throws Exception { try { - var report = Optional.ofNullable( - ciiSchematron.applySchematronValidationToSVRL( - new ByteArrayWrapper( - validationRequest - .xml() - .getBytes(validationRequest.xmlCharset()), - false - ) + byte[] bytes = validationRequest + .xml() + .getBytes(validationRequest.xmlCharset()); + + var en16931Report = Optional.ofNullable( + en16931Schematron.applySchematronValidationToSVRL( + new ByteArrayWrapper(bytes, false) + ) + ); + + if (en16931Report.isEmpty()) { + return Optional.empty(); + } + + var facutrxReport = Optional.ofNullable( + facturXSchematron.applySchematronValidationToSVRL( + new ByteArrayWrapper(bytes, false) ) ); - return report.map(schematronOutputType -> + return facutrxReport.map(schematronOutputType -> ValidationResult.of( XmlProfileType.FACTURX_EXTENDED, validationRequest, + en16931Report.get(), schematronOutputType ) ); diff --git a/src/main/java/io/github/easybill/Services/Validators/PeppolSchematronValidator.java b/src/main/java/io/github/easybill/Services/Validators/PeppolSchematronValidator.java index 7181852..b7f6970 100644 --- a/src/main/java/io/github/easybill/Services/Validators/PeppolSchematronValidator.java +++ b/src/main/java/io/github/easybill/Services/Validators/PeppolSchematronValidator.java @@ -15,15 +15,27 @@ @Singleton public final class PeppolSchematronValidator implements ISchematronValidator { - private final SchematronResourceSCH ublSchematron; + private final SchematronResourceSCH en16931Schematron; + private final SchematronResourceSCH peppolBisSchematron; public PeppolSchematronValidator() { - ublSchematron = + en16931Schematron = + new SchematronResourceSCH( + new ClassPathResource("/EN16931/EN16931_1.3.13_UBL.sch") + ); + + peppolBisSchematron = new SchematronResourceSCH( new ClassPathResource("/Peppol/PEPPOL_BIS_BILLING_3.0.sch") ); - if (!ublSchematron.isValidSchematron()) { + if (!en16931Schematron.isValidSchematron()) { + throw new RuntimeException( + "Schematron validation for EN16931 UBL failed" + ); + } + + if (!peppolBisSchematron.isValidSchematron()) { throw new RuntimeException( "Schematron validation for Peppol UBL failed" ); @@ -32,7 +44,7 @@ public PeppolSchematronValidator() { @Override public boolean validateSchematron() { - return ublSchematron.isValidSchematron(); + return peppolBisSchematron.isValidSchematron(); } @Override @@ -48,21 +60,31 @@ public Optional validate( ValidationRequest validationRequest ) throws Exception { try { - var report = Optional.ofNullable( - ublSchematron.applySchematronValidationToSVRL( - new ByteArrayWrapper( - validationRequest - .xml() - .getBytes(validationRequest.xmlCharset()), - false - ) + byte[] bytes = validationRequest + .xml() + .getBytes(validationRequest.xmlCharset()); + + var en16931Report = Optional.ofNullable( + en16931Schematron.applySchematronValidationToSVRL( + new ByteArrayWrapper(bytes, false) + ) + ); + + if (en16931Report.isEmpty()) { + return Optional.empty(); + } + + var peppolReport = Optional.ofNullable( + peppolBisSchematron.applySchematronValidationToSVRL( + new ByteArrayWrapper(bytes, false) ) ); - return report.map(schematronOutputType -> + return peppolReport.map(schematronOutputType -> ValidationResult.of( XmlProfileType.PEPPOL_30, validationRequest, + en16931Report.get(), schematronOutputType ) ); diff --git a/src/main/java/io/github/easybill/Services/Validators/XRechnungSchematronValidator.java b/src/main/java/io/github/easybill/Services/Validators/XRechnungSchematronValidator.java index da16a54..3632547 100644 --- a/src/main/java/io/github/easybill/Services/Validators/XRechnungSchematronValidator.java +++ b/src/main/java/io/github/easybill/Services/Validators/XRechnungSchematronValidator.java @@ -15,27 +15,51 @@ public final class XRechnungSchematronValidator implements ISchematronValidator { - private final SchematronResourceSCH ciiSchematron; - private final SchematronResourceSCH ublSchematron; + private final SchematronResourceSCH en16931CiiSchematron; + private final SchematronResourceSCH en16931UblSchematron; + private final SchematronResourceSCH xRechnungCiiSchematron; + private final SchematronResourceSCH xRechnungUblSchematron; public XRechnungSchematronValidator() { - ciiSchematron = + en16931CiiSchematron = + new SchematronResourceSCH( + new ClassPathResource("/EN16931/EN16931_1.3.13_CII.sch") + ); + + en16931UblSchematron = + new SchematronResourceSCH( + new ClassPathResource("/EN16931/EN16931_1.3.13_UBL.sch") + ); + + xRechnungCiiSchematron = new SchematronResourceSCH( new ClassPathResource("/XRechnung/XRechnung_3.2_CII.sch") ); - ublSchematron = + xRechnungUblSchematron = new SchematronResourceSCH( new ClassPathResource("/XRechnung/XRechnung_3.2_UBL.sch") ); - if (!ciiSchematron.isValidSchematron()) { + if (!en16931CiiSchematron.isValidSchematron()) { + throw new RuntimeException( + "Schematron validation for EN16931 CII failed" + ); + } + + if (!en16931UblSchematron.isValidSchematron()) { + throw new RuntimeException( + "Schematron validation for EN16931 UBL failed" + ); + } + + if (!xRechnungCiiSchematron.isValidSchematron()) { throw new RuntimeException( "Schematron validation for XRechnung CII failed" ); } - if (!ublSchematron.isValidSchematron()) { + if (!xRechnungUblSchematron.isValidSchematron()) { throw new RuntimeException( "Schematron validation for XRechnung UBL failed" ); @@ -44,11 +68,11 @@ public XRechnungSchematronValidator() { @Override public boolean validateSchematron() { - if (!ciiSchematron.isValidSchematron()) { + if (!xRechnungCiiSchematron.isValidSchematron()) { return false; } - if (!ublSchematron.isValidSchematron()) { + if (!xRechnungUblSchematron.isValidSchematron()) { return false; } @@ -72,24 +96,43 @@ public Optional validate( ); try { - var report = + var en16931Report = + switch (validationRequest.xmlSyntaxType()) { + case CII -> Optional.ofNullable( + en16931CiiSchematron.applySchematronValidationToSVRL( + bytesWrapper + ) + ); + case UBL -> Optional.ofNullable( + en16931UblSchematron.applySchematronValidationToSVRL( + bytesWrapper + ) + ); + }; + + if (en16931Report.isEmpty()) { + return Optional.empty(); + } + + var xRechnungReport = switch (validationRequest.xmlSyntaxType()) { case CII -> Optional.ofNullable( - ciiSchematron.applySchematronValidationToSVRL( + xRechnungCiiSchematron.applySchematronValidationToSVRL( bytesWrapper ) ); case UBL -> Optional.ofNullable( - ublSchematron.applySchematronValidationToSVRL( + xRechnungUblSchematron.applySchematronValidationToSVRL( bytesWrapper ) ); }; - return report.map(schematronOutputType -> + return xRechnungReport.map(schematronOutputType -> ValidationResult.of( XmlProfileType.XRECHNUNG_30, validationRequest, + en16931Report.get(), schematronOutputType ) ); diff --git a/src/main/resources/EN16931/EN16931_1.3.13_CII.sch b/src/main/resources/EN16931/EN16931_1.3.13_CII.sch index 91a34f5..cb542c7 100755 --- a/src/main/resources/EN16931/EN16931_1.3.13_CII.sch +++ b/src/main/resources/EN16931/EN16931_1.3.13_CII.sch @@ -825,8 +825,8 @@ [CII-SR-420] - NetIncludingTaxesLineTotalAmount should not be present - [CII-DT-013] - languageID should not be present - [CII-DT-014] - languageLocaleID should not be present + [CII-DT-013] - languageID should not be present + [CII-DT-014] - languageLocaleID should not be present [CII-SR-438] - ValuationBreakdownStatement should not be present @@ -834,28 +834,28 @@ [CII-SR-005] - SpecifiedDocumentVersion should not be present - [CII-DT-001] - schemeName should not be present - [CII-DT-002] - schemeAgencyName should not be present - [CII-DT-003] - schemeDataURI should not be present - [CII-DT-004] - schemeURI should not be present - [CII-DT-005] - schemeID should not be present - [CII-DT-006] - schemeAgencyID should not be present - [CII-DT-007] - schemeVersionID should not be present + [CII-DT-001] - schemeName should not be present + [CII-DT-002] - schemeAgencyName should not be present + [CII-DT-003] - schemeDataURI should not be present + [CII-DT-004] - schemeURI should not be present + [CII-DT-005] - schemeID should not be present + [CII-DT-006] - schemeAgencyID should not be present + [CII-DT-007] - schemeVersionID should not be present - [CII-DT-001] - schemeName should not be present - [CII-DT-002] - schemeAgencyName should not be present - [CII-DT-003] - schemeDataURI should not be present - [CII-DT-004] - schemeURI should not be present + [CII-DT-001] - schemeName should not be present + [CII-DT-002] - schemeAgencyName should not be present + [CII-DT-003] - schemeDataURI should not be present + [CII-DT-004] - schemeURI should not be present - [CII-DT-008] - name should not be present - [CII-DT-009] - listURI should not be present + [CII-DT-008] - name should not be present + [CII-DT-009] - listURI should not be present - [CII-DT-010] - listID should not be present - [CII-DT-011] - listAgencyID should not be present - [CII-DT-012] - listVersionID should not be present + [CII-DT-010] - listID should not be present + [CII-DT-011] - listAgencyID should not be present + [CII-DT-012] - listVersionID should not be present [CII-DT-045] - @listID should not be present @@ -864,32 +864,32 @@ [CII-DT-048] - @listURI should not be present - [CII-DT-015] - URIID should not be present - [CII-DT-016] - StatusCode should not be present - [CII-DT-017] - CopyIndicator should not be present - [CII-DT-018] - TypeCode should not be present - [CII-DT-019] - GlobalID should not be present - [CII-DT-020] - RevisionID should not be present - [CII-DT-021] - Name should not be present - [CII-DT-022] - AttachmentBinaryObject should not be present - [CII-DT-023] - Information should not be present - [CII-DT-024] - ReferenceTypeCode should not be present - [CII-DT-025] - SectionName should not be present - [CII-DT-026] - PreviousRevisionID should not be present - [CII-DT-027] - FormattedIssueDateTime should not be present - [CII-DT-028] - EffectiveSpecifiedPeriod should not be present - [CII-DT-029] - IssuerTradeParty should not be present - [CII-DT-030] - AttachedSpecifiedBinaryFile should not be present + [CII-DT-015] - URIID should not be present + [CII-DT-016] - StatusCode should not be present + [CII-DT-017] - CopyIndicator should not be present + [CII-DT-018] - TypeCode should not be present + [CII-DT-019] - GlobalID should not be present + [CII-DT-020] - RevisionID should not be present + [CII-DT-021] - Name should not be present + [CII-DT-022] - AttachmentBinaryObject should not be present + [CII-DT-023] - Information should not be present + [CII-DT-024] - ReferenceTypeCode should not be present + [CII-DT-025] - SectionName should not be present + [CII-DT-026] - PreviousRevisionID should not be present + [CII-DT-027] - FormattedIssueDateTime should not be present + [CII-DT-028] - EffectiveSpecifiedPeriod should not be present + [CII-DT-029] - IssuerTradeParty should not be present + [CII-DT-030] - AttachedSpecifiedBinaryFile should not be present - [CII-DT-031] - currencyID should not be present - [CII-DT-032] - currencyCodeListVersionID should not be present + [CII-DT-031] - currencyID should not be present + [CII-DT-032] - currencyCodeListVersionID should not be present - [CII-DT-033] - unitCode should not be present - [CII-DT-034] - unitCodeListID should not be present - [CII-DT-035] - unitCodeListAgencyID should not be present - [CII-DT-036] - unitCodeListAgencyName should not be present + [CII-DT-033] - unitCode should not be present + [CII-DT-034] - unitCodeListID should not be present + [CII-DT-035] - unitCodeListAgencyID should not be present + [CII-DT-036] - unitCodeListAgencyName should not be present [CII-DT-037] - TypeCode shall be 'VAT' From 5db377bebcb28bf7e548def1e74ec2ea66d1f9f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20N=C3=B6hles?= Date: Thu, 30 Jan 2025 12:37:36 +0100 Subject: [PATCH 2/7] Add Validator Chain --- .../easybill/Dtos/ValidationResult.java | 8 ++------ .../github/easybill/Dtos/ValidatorResult.java | 11 ++++++++++- .../EN16931SchematronValidator.java | 10 +++++++++- .../FacturXSchematronValidator.java | 13 +++++++++++-- .../Validators/PeppolSchematronValidator.java | 13 +++++++++++-- .../XRechnungSchematronValidator.java | 19 +++++++++++++++++-- 6 files changed, 60 insertions(+), 14 deletions(-) diff --git a/src/main/java/io/github/easybill/Dtos/ValidationResult.java b/src/main/java/io/github/easybill/Dtos/ValidationResult.java index e06ba34..1a09fad 100644 --- a/src/main/java/io/github/easybill/Dtos/ValidationResult.java +++ b/src/main/java/io/github/easybill/Dtos/ValidationResult.java @@ -1,7 +1,6 @@ package io.github.easybill.Dtos; import com.fasterxml.jackson.annotation.JsonProperty; -import com.helger.schematron.svrl.jaxb.SchematronOutputType; import io.github.easybill.Enums.XmlProfileType; import java.util.Arrays; import java.util.List; @@ -23,17 +22,14 @@ public boolean isValid() { public static ValidationResult of( XmlProfileType xmlProfileType, ValidationRequest validationRequest, - SchematronOutputType... schematronOutputTypes + ValidatorResult... validatorResults ) { return new ValidationResult( new ValidationResultMetaData( validationRequest.xmlSyntaxType(), xmlProfileType ), - Arrays - .stream(schematronOutputTypes) - .map(ValidatorResult::of) - .toList() + Arrays.stream(validatorResults).toList() ); } } diff --git a/src/main/java/io/github/easybill/Dtos/ValidatorResult.java b/src/main/java/io/github/easybill/Dtos/ValidatorResult.java index d424d37..252c3f0 100644 --- a/src/main/java/io/github/easybill/Dtos/ValidatorResult.java +++ b/src/main/java/io/github/easybill/Dtos/ValidatorResult.java @@ -1,5 +1,6 @@ package io.github.easybill.Dtos; +import com.fasterxml.jackson.annotation.JsonProperty; import com.helger.schematron.svrl.jaxb.FailedAssert; import com.helger.schematron.svrl.jaxb.SchematronOutputType; import java.util.Collections; @@ -8,6 +9,8 @@ import org.checkerframework.checker.nullness.qual.NonNull; public record ValidatorResult( + @JsonProperty("name") @NonNull String name, + @JsonProperty("artifact_version") @NonNull String version, @NonNull List<@NonNull ValidationResultField> errors, @NonNull List<@NonNull ValidationResultField> warnings ) { @@ -16,8 +19,14 @@ public record ValidatorResult( warnings = Collections.unmodifiableList(warnings); } - public static ValidatorResult of(@NonNull SchematronOutputType report) { + public static ValidatorResult of( + @NonNull String name, + @NonNull String version, + @NonNull SchematronOutputType report + ) { return new ValidatorResult( + name, + version, getErrorsFromSchematronOutput(report), getWarningsFromSchematronOutput(report) ); diff --git a/src/main/java/io/github/easybill/Services/Validators/EN16931SchematronValidator.java b/src/main/java/io/github/easybill/Services/Validators/EN16931SchematronValidator.java index 387d6be..2b6fe5a 100644 --- a/src/main/java/io/github/easybill/Services/Validators/EN16931SchematronValidator.java +++ b/src/main/java/io/github/easybill/Services/Validators/EN16931SchematronValidator.java @@ -6,6 +6,7 @@ import io.github.easybill.Contracts.ISchematronValidator; import io.github.easybill.Dtos.ValidationRequest; import io.github.easybill.Dtos.ValidationResult; +import io.github.easybill.Dtos.ValidatorResult; import io.github.easybill.Enums.XmlProfileType; import io.github.easybill.Exceptions.ParsingException; import jakarta.inject.Singleton; @@ -87,7 +88,14 @@ public Optional validate( ValidationResult.of( XmlProfileType.EN16931, validationRequest, - schematronOutputType + ValidatorResult.of( + String.format( + "EN16931 %s", + validationRequest.xmlSyntaxType() + ), + "1.3.13", + schematronOutputType + ) ) ); } catch (IllegalArgumentException exception) { diff --git a/src/main/java/io/github/easybill/Services/Validators/FacturXSchematronValidator.java b/src/main/java/io/github/easybill/Services/Validators/FacturXSchematronValidator.java index 215fc5b..3ae74a5 100644 --- a/src/main/java/io/github/easybill/Services/Validators/FacturXSchematronValidator.java +++ b/src/main/java/io/github/easybill/Services/Validators/FacturXSchematronValidator.java @@ -6,6 +6,7 @@ import io.github.easybill.Contracts.ISchematronValidator; import io.github.easybill.Dtos.ValidationRequest; import io.github.easybill.Dtos.ValidationResult; +import io.github.easybill.Dtos.ValidatorResult; import io.github.easybill.Enums.XMLSyntaxType; import io.github.easybill.Enums.XmlProfileType; import io.github.easybill.Exceptions.ParsingException; @@ -85,8 +86,16 @@ public Optional validate( ValidationResult.of( XmlProfileType.FACTURX_EXTENDED, validationRequest, - en16931Report.get(), - schematronOutputType + ValidatorResult.of( + "EN16931 CII", + "1.3.13", + en16931Report.get() + ), + ValidatorResult.of( + "factur-x CII EXTENDED", + "1.07.2", + schematronOutputType + ) ) ); } catch (IllegalArgumentException exception) { diff --git a/src/main/java/io/github/easybill/Services/Validators/PeppolSchematronValidator.java b/src/main/java/io/github/easybill/Services/Validators/PeppolSchematronValidator.java index b7f6970..792749f 100644 --- a/src/main/java/io/github/easybill/Services/Validators/PeppolSchematronValidator.java +++ b/src/main/java/io/github/easybill/Services/Validators/PeppolSchematronValidator.java @@ -6,6 +6,7 @@ import io.github.easybill.Contracts.ISchematronValidator; import io.github.easybill.Dtos.ValidationRequest; import io.github.easybill.Dtos.ValidationResult; +import io.github.easybill.Dtos.ValidatorResult; import io.github.easybill.Enums.XMLSyntaxType; import io.github.easybill.Enums.XmlProfileType; import io.github.easybill.Exceptions.ParsingException; @@ -84,8 +85,16 @@ public Optional validate( ValidationResult.of( XmlProfileType.PEPPOL_30, validationRequest, - en16931Report.get(), - schematronOutputType + ValidatorResult.of( + "EN16931 UBL", + "1.3.13", + en16931Report.get() + ), + ValidatorResult.of( + "Peppol BIS UBL", + "3.0", + schematronOutputType + ) ) ); } catch (IllegalArgumentException exception) { diff --git a/src/main/java/io/github/easybill/Services/Validators/XRechnungSchematronValidator.java b/src/main/java/io/github/easybill/Services/Validators/XRechnungSchematronValidator.java index 3632547..f22bd07 100644 --- a/src/main/java/io/github/easybill/Services/Validators/XRechnungSchematronValidator.java +++ b/src/main/java/io/github/easybill/Services/Validators/XRechnungSchematronValidator.java @@ -6,6 +6,7 @@ import io.github.easybill.Contracts.ISchematronValidator; import io.github.easybill.Dtos.ValidationRequest; import io.github.easybill.Dtos.ValidationResult; +import io.github.easybill.Dtos.ValidatorResult; import io.github.easybill.Enums.XmlProfileType; import io.github.easybill.Exceptions.ParsingException; import jakarta.inject.Singleton; @@ -132,8 +133,22 @@ public Optional validate( ValidationResult.of( XmlProfileType.XRECHNUNG_30, validationRequest, - en16931Report.get(), - schematronOutputType + ValidatorResult.of( + String.format( + "EN16931 %s", + validationRequest.xmlSyntaxType() + ), + "1.3.13", + en16931Report.get() + ), + ValidatorResult.of( + String.format( + "XRechnung %s", + validationRequest.xmlSyntaxType() + ), + "3.2", + schematronOutputType + ) ) ); } catch (IllegalArgumentException exception) { From 195d8d117c78059e84a5b7543c9c2cdd3e24e1e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20N=C3=B6hles?= Date: Thu, 30 Jan 2025 12:51:49 +0100 Subject: [PATCH 3/7] Add Validator Chain --- .../easybill/Dtos/ValidationResult.java | 3 +- .../github/easybill/Dtos/ValidatorResult.java | 64 -------------- .../EN16931ValidatorResult.java | 29 +++++++ .../FacturXValidatorResult.java | 29 +++++++ .../PeppolValidatorResult.java | 29 +++++++ .../ValidatorResults/ValidatorResult.java | 83 +++++++++++++++++++ .../XRechnungValidatorResult.java | 29 +++++++ .../EN16931SchematronValidator.java | 11 +-- .../FacturXSchematronValidator.java | 15 +--- .../Validators/PeppolSchematronValidator.java | 15 +--- .../XRechnungSchematronValidator.java | 21 +---- 11 files changed, 215 insertions(+), 113 deletions(-) delete mode 100644 src/main/java/io/github/easybill/Dtos/ValidatorResult.java create mode 100644 src/main/java/io/github/easybill/Dtos/ValidatorResults/EN16931ValidatorResult.java create mode 100644 src/main/java/io/github/easybill/Dtos/ValidatorResults/FacturXValidatorResult.java create mode 100644 src/main/java/io/github/easybill/Dtos/ValidatorResults/PeppolValidatorResult.java create mode 100644 src/main/java/io/github/easybill/Dtos/ValidatorResults/ValidatorResult.java create mode 100644 src/main/java/io/github/easybill/Dtos/ValidatorResults/XRechnungValidatorResult.java diff --git a/src/main/java/io/github/easybill/Dtos/ValidationResult.java b/src/main/java/io/github/easybill/Dtos/ValidationResult.java index 1a09fad..ac755a0 100644 --- a/src/main/java/io/github/easybill/Dtos/ValidationResult.java +++ b/src/main/java/io/github/easybill/Dtos/ValidationResult.java @@ -1,6 +1,7 @@ package io.github.easybill.Dtos; import com.fasterxml.jackson.annotation.JsonProperty; +import io.github.easybill.Dtos.ValidatorResults.ValidatorResult; import io.github.easybill.Enums.XmlProfileType; import java.util.Arrays; import java.util.List; @@ -16,7 +17,7 @@ public record ValidationResult( public boolean isValid() { return validationResults .stream() - .allMatch(element -> element.errors().isEmpty()); + .allMatch(element -> element.getErrors().isEmpty()); } public static ValidationResult of( diff --git a/src/main/java/io/github/easybill/Dtos/ValidatorResult.java b/src/main/java/io/github/easybill/Dtos/ValidatorResult.java deleted file mode 100644 index 252c3f0..0000000 --- a/src/main/java/io/github/easybill/Dtos/ValidatorResult.java +++ /dev/null @@ -1,64 +0,0 @@ -package io.github.easybill.Dtos; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.helger.schematron.svrl.jaxb.FailedAssert; -import com.helger.schematron.svrl.jaxb.SchematronOutputType; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import org.checkerframework.checker.nullness.qual.NonNull; - -public record ValidatorResult( - @JsonProperty("name") @NonNull String name, - @JsonProperty("artifact_version") @NonNull String version, - @NonNull List<@NonNull ValidationResultField> errors, - @NonNull List<@NonNull ValidationResultField> warnings -) { - public ValidatorResult { - errors = Collections.unmodifiableList(errors); - warnings = Collections.unmodifiableList(warnings); - } - - public static ValidatorResult of( - @NonNull String name, - @NonNull String version, - @NonNull SchematronOutputType report - ) { - return new ValidatorResult( - name, - version, - getErrorsFromSchematronOutput(report), - getWarningsFromSchematronOutput(report) - ); - } - - private static List<@NonNull ValidationResultField> getErrorsFromSchematronOutput( - @NonNull SchematronOutputType outputType - ) { - return outputType - .getActivePatternAndFiredRuleAndFailedAssert() - .stream() - .filter(element -> element instanceof FailedAssert) - .filter(element -> - Objects.equals(((FailedAssert) element).getFlag(), "fatal") - ) - .map(element -> (FailedAssert) element) - .map(ValidationResultField::fromFailedAssert) - .toList(); - } - - private static List<@NonNull ValidationResultField> getWarningsFromSchematronOutput( - @NonNull SchematronOutputType outputType - ) { - return outputType - .getActivePatternAndFiredRuleAndFailedAssert() - .stream() - .filter(element -> element instanceof FailedAssert) - .filter(element -> - Objects.equals(((FailedAssert) element).getFlag(), "warning") - ) - .map(element -> (FailedAssert) element) - .map(ValidationResultField::fromFailedAssert) - .toList(); - } -} diff --git a/src/main/java/io/github/easybill/Dtos/ValidatorResults/EN16931ValidatorResult.java b/src/main/java/io/github/easybill/Dtos/ValidatorResults/EN16931ValidatorResult.java new file mode 100644 index 0000000..a63ad22 --- /dev/null +++ b/src/main/java/io/github/easybill/Dtos/ValidatorResults/EN16931ValidatorResult.java @@ -0,0 +1,29 @@ +package io.github.easybill.Dtos.ValidatorResults; + +import com.helger.schematron.svrl.jaxb.SchematronOutputType; +import io.github.easybill.Dtos.ValidationResultField; +import java.util.List; +import org.checkerframework.checker.nullness.qual.NonNull; + +public final class EN16931ValidatorResult extends ValidatorResult { + + public EN16931ValidatorResult( + @NonNull String name, + @NonNull String version, + @NonNull List<@NonNull ValidationResultField> errors, + @NonNull List<@NonNull ValidationResultField> warnings + ) { + super(name, version, errors, warnings); + } + + public static EN16931ValidatorResult of( + @NonNull SchematronOutputType report + ) { + return new EN16931ValidatorResult( + "EN16931", + "1.3.13", + getErrorsFromSchematronOutput(report), + getWarningsFromSchematronOutput(report) + ); + } +} diff --git a/src/main/java/io/github/easybill/Dtos/ValidatorResults/FacturXValidatorResult.java b/src/main/java/io/github/easybill/Dtos/ValidatorResults/FacturXValidatorResult.java new file mode 100644 index 0000000..6a325ad --- /dev/null +++ b/src/main/java/io/github/easybill/Dtos/ValidatorResults/FacturXValidatorResult.java @@ -0,0 +1,29 @@ +package io.github.easybill.Dtos.ValidatorResults; + +import com.helger.schematron.svrl.jaxb.SchematronOutputType; +import io.github.easybill.Dtos.ValidationResultField; +import java.util.List; +import org.checkerframework.checker.nullness.qual.NonNull; + +public final class FacturXValidatorResult extends ValidatorResult { + + public FacturXValidatorResult( + @NonNull String name, + @NonNull String version, + @NonNull List<@NonNull ValidationResultField> errors, + @NonNull List<@NonNull ValidationResultField> warnings + ) { + super(name, version, errors, warnings); + } + + public static FacturXValidatorResult of( + @NonNull SchematronOutputType report + ) { + return new FacturXValidatorResult( + "factur-x", + "1.07.2", + getErrorsFromSchematronOutput(report), + getWarningsFromSchematronOutput(report) + ); + } +} diff --git a/src/main/java/io/github/easybill/Dtos/ValidatorResults/PeppolValidatorResult.java b/src/main/java/io/github/easybill/Dtos/ValidatorResults/PeppolValidatorResult.java new file mode 100644 index 0000000..1871901 --- /dev/null +++ b/src/main/java/io/github/easybill/Dtos/ValidatorResults/PeppolValidatorResult.java @@ -0,0 +1,29 @@ +package io.github.easybill.Dtos.ValidatorResults; + +import com.helger.schematron.svrl.jaxb.SchematronOutputType; +import io.github.easybill.Dtos.ValidationResultField; +import java.util.List; +import org.checkerframework.checker.nullness.qual.NonNull; + +public final class PeppolValidatorResult extends ValidatorResult { + + public PeppolValidatorResult( + @NonNull String name, + @NonNull String version, + @NonNull List<@NonNull ValidationResultField> errors, + @NonNull List<@NonNull ValidationResultField> warnings + ) { + super(name, version, errors, warnings); + } + + public static PeppolValidatorResult of( + @NonNull SchematronOutputType report + ) { + return new PeppolValidatorResult( + "Peppol BIS", + "3.0", + getErrorsFromSchematronOutput(report), + getWarningsFromSchematronOutput(report) + ); + } +} diff --git a/src/main/java/io/github/easybill/Dtos/ValidatorResults/ValidatorResult.java b/src/main/java/io/github/easybill/Dtos/ValidatorResults/ValidatorResult.java new file mode 100644 index 0000000..e7eaeb0 --- /dev/null +++ b/src/main/java/io/github/easybill/Dtos/ValidatorResults/ValidatorResult.java @@ -0,0 +1,83 @@ +package io.github.easybill.Dtos.ValidatorResults; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.helger.schematron.svrl.jaxb.FailedAssert; +import com.helger.schematron.svrl.jaxb.SchematronOutputType; +import io.github.easybill.Dtos.ValidationResultField; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import org.checkerframework.checker.nullness.qual.NonNull; + +public abstract class ValidatorResult { + + @NonNull + private final String name; + + @NonNull + private final String version; + + @NonNull + private final List<@NonNull ValidationResultField> warnings; + + @NonNull + private final List<@NonNull ValidationResultField> errors; + + public ValidatorResult( + @JsonProperty("name") @NonNull String name, + @JsonProperty("artifact_version") @NonNull String version, + @NonNull List<@NonNull ValidationResultField> errors, + @NonNull List<@NonNull ValidationResultField> warnings + ) { + this.name = name; + this.version = version; + this.errors = Collections.unmodifiableList(errors); + this.warnings = Collections.unmodifiableList(warnings); + } + + static List<@NonNull ValidationResultField> getErrorsFromSchematronOutput( + @NonNull SchematronOutputType outputType + ) { + return outputType + .getActivePatternAndFiredRuleAndFailedAssert() + .stream() + .filter(element -> element instanceof FailedAssert) + .filter(element -> + Objects.equals(((FailedAssert) element).getFlag(), "fatal") + ) + .map(element -> (FailedAssert) element) + .map(ValidationResultField::fromFailedAssert) + .toList(); + } + + static List<@NonNull ValidationResultField> getWarningsFromSchematronOutput( + @NonNull SchematronOutputType outputType + ) { + return outputType + .getActivePatternAndFiredRuleAndFailedAssert() + .stream() + .filter(element -> element instanceof FailedAssert) + .filter(element -> + Objects.equals(((FailedAssert) element).getFlag(), "warning") + ) + .map(element -> (FailedAssert) element) + .map(ValidationResultField::fromFailedAssert) + .toList(); + } + + public @NonNull List<@NonNull ValidationResultField> getErrors() { + return errors; + } + + public @NonNull List<@NonNull ValidationResultField> getWarnings() { + return warnings; + } + + public @NonNull String getName() { + return name; + } + + public @NonNull String getVersion() { + return version; + } +} diff --git a/src/main/java/io/github/easybill/Dtos/ValidatorResults/XRechnungValidatorResult.java b/src/main/java/io/github/easybill/Dtos/ValidatorResults/XRechnungValidatorResult.java new file mode 100644 index 0000000..1a29e28 --- /dev/null +++ b/src/main/java/io/github/easybill/Dtos/ValidatorResults/XRechnungValidatorResult.java @@ -0,0 +1,29 @@ +package io.github.easybill.Dtos.ValidatorResults; + +import com.helger.schematron.svrl.jaxb.SchematronOutputType; +import io.github.easybill.Dtos.ValidationResultField; +import java.util.List; +import org.checkerframework.checker.nullness.qual.NonNull; + +public final class XRechnungValidatorResult extends ValidatorResult { + + public XRechnungValidatorResult( + @NonNull String name, + @NonNull String version, + @NonNull List<@NonNull ValidationResultField> errors, + @NonNull List<@NonNull ValidationResultField> warnings + ) { + super(name, version, errors, warnings); + } + + public static XRechnungValidatorResult of( + @NonNull SchematronOutputType report + ) { + return new XRechnungValidatorResult( + "XRechnung", + "3.2", + getErrorsFromSchematronOutput(report), + getWarningsFromSchematronOutput(report) + ); + } +} diff --git a/src/main/java/io/github/easybill/Services/Validators/EN16931SchematronValidator.java b/src/main/java/io/github/easybill/Services/Validators/EN16931SchematronValidator.java index 2b6fe5a..e39318b 100644 --- a/src/main/java/io/github/easybill/Services/Validators/EN16931SchematronValidator.java +++ b/src/main/java/io/github/easybill/Services/Validators/EN16931SchematronValidator.java @@ -6,7 +6,7 @@ import io.github.easybill.Contracts.ISchematronValidator; import io.github.easybill.Dtos.ValidationRequest; import io.github.easybill.Dtos.ValidationResult; -import io.github.easybill.Dtos.ValidatorResult; +import io.github.easybill.Dtos.ValidatorResults.EN16931ValidatorResult; import io.github.easybill.Enums.XmlProfileType; import io.github.easybill.Exceptions.ParsingException; import jakarta.inject.Singleton; @@ -88,14 +88,7 @@ public Optional validate( ValidationResult.of( XmlProfileType.EN16931, validationRequest, - ValidatorResult.of( - String.format( - "EN16931 %s", - validationRequest.xmlSyntaxType() - ), - "1.3.13", - schematronOutputType - ) + EN16931ValidatorResult.of(schematronOutputType) ) ); } catch (IllegalArgumentException exception) { diff --git a/src/main/java/io/github/easybill/Services/Validators/FacturXSchematronValidator.java b/src/main/java/io/github/easybill/Services/Validators/FacturXSchematronValidator.java index 3ae74a5..329b420 100644 --- a/src/main/java/io/github/easybill/Services/Validators/FacturXSchematronValidator.java +++ b/src/main/java/io/github/easybill/Services/Validators/FacturXSchematronValidator.java @@ -6,7 +6,8 @@ import io.github.easybill.Contracts.ISchematronValidator; import io.github.easybill.Dtos.ValidationRequest; import io.github.easybill.Dtos.ValidationResult; -import io.github.easybill.Dtos.ValidatorResult; +import io.github.easybill.Dtos.ValidatorResults.EN16931ValidatorResult; +import io.github.easybill.Dtos.ValidatorResults.FacturXValidatorResult; import io.github.easybill.Enums.XMLSyntaxType; import io.github.easybill.Enums.XmlProfileType; import io.github.easybill.Exceptions.ParsingException; @@ -86,16 +87,8 @@ public Optional validate( ValidationResult.of( XmlProfileType.FACTURX_EXTENDED, validationRequest, - ValidatorResult.of( - "EN16931 CII", - "1.3.13", - en16931Report.get() - ), - ValidatorResult.of( - "factur-x CII EXTENDED", - "1.07.2", - schematronOutputType - ) + EN16931ValidatorResult.of(en16931Report.get()), + FacturXValidatorResult.of(schematronOutputType) ) ); } catch (IllegalArgumentException exception) { diff --git a/src/main/java/io/github/easybill/Services/Validators/PeppolSchematronValidator.java b/src/main/java/io/github/easybill/Services/Validators/PeppolSchematronValidator.java index 792749f..0dc6459 100644 --- a/src/main/java/io/github/easybill/Services/Validators/PeppolSchematronValidator.java +++ b/src/main/java/io/github/easybill/Services/Validators/PeppolSchematronValidator.java @@ -6,7 +6,8 @@ import io.github.easybill.Contracts.ISchematronValidator; import io.github.easybill.Dtos.ValidationRequest; import io.github.easybill.Dtos.ValidationResult; -import io.github.easybill.Dtos.ValidatorResult; +import io.github.easybill.Dtos.ValidatorResults.EN16931ValidatorResult; +import io.github.easybill.Dtos.ValidatorResults.PeppolValidatorResult; import io.github.easybill.Enums.XMLSyntaxType; import io.github.easybill.Enums.XmlProfileType; import io.github.easybill.Exceptions.ParsingException; @@ -85,16 +86,8 @@ public Optional validate( ValidationResult.of( XmlProfileType.PEPPOL_30, validationRequest, - ValidatorResult.of( - "EN16931 UBL", - "1.3.13", - en16931Report.get() - ), - ValidatorResult.of( - "Peppol BIS UBL", - "3.0", - schematronOutputType - ) + EN16931ValidatorResult.of(en16931Report.get()), + PeppolValidatorResult.of(schematronOutputType) ) ); } catch (IllegalArgumentException exception) { diff --git a/src/main/java/io/github/easybill/Services/Validators/XRechnungSchematronValidator.java b/src/main/java/io/github/easybill/Services/Validators/XRechnungSchematronValidator.java index f22bd07..72c860a 100644 --- a/src/main/java/io/github/easybill/Services/Validators/XRechnungSchematronValidator.java +++ b/src/main/java/io/github/easybill/Services/Validators/XRechnungSchematronValidator.java @@ -6,7 +6,8 @@ import io.github.easybill.Contracts.ISchematronValidator; import io.github.easybill.Dtos.ValidationRequest; import io.github.easybill.Dtos.ValidationResult; -import io.github.easybill.Dtos.ValidatorResult; +import io.github.easybill.Dtos.ValidatorResults.EN16931ValidatorResult; +import io.github.easybill.Dtos.ValidatorResults.XRechnungValidatorResult; import io.github.easybill.Enums.XmlProfileType; import io.github.easybill.Exceptions.ParsingException; import jakarta.inject.Singleton; @@ -133,22 +134,8 @@ public Optional validate( ValidationResult.of( XmlProfileType.XRECHNUNG_30, validationRequest, - ValidatorResult.of( - String.format( - "EN16931 %s", - validationRequest.xmlSyntaxType() - ), - "1.3.13", - en16931Report.get() - ), - ValidatorResult.of( - String.format( - "XRechnung %s", - validationRequest.xmlSyntaxType() - ), - "3.2", - schematronOutputType - ) + EN16931ValidatorResult.of(en16931Report.get()), + XRechnungValidatorResult.of(schematronOutputType) ) ); } catch (IllegalArgumentException exception) { From 84a984ff1ed4331d072dacaca3ce15d699505439 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20N=C3=B6hles?= Date: Thu, 30 Jan 2025 12:53:05 +0100 Subject: [PATCH 4/7] Update readme --- README.md | 54 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 6ca1522..c5f079e 100644 --- a/README.md +++ b/README.md @@ -74,11 +74,23 @@ final class EN16931Validator ```JSON { "meta": { - "xml_syntax_type": "CII", - "xml_profile_type": "FACTURX_EXTENDED" + "xml_syntax_type": "UBL", + "xml_profile_type": "PEPPOL_30" }, - "errors": [], - "warnings": [], + "validation_results": [ + { + "name": "EN16931", + "version": "1.3.13", + "warnings": [], + "errors": [] + }, + { + "name": "Peppol BIS", + "version": "3.0", + "warnings": [], + "errors": [] + } + ], "is_valid": true } ``` @@ -89,28 +101,32 @@ final class EN16931Validator ```JSON { "meta": { - "xml_syntax_type": "CII", - "xml_profile_type": "EN16931" + "xml_syntax_type": "UBL", + "xml_profile_type": "PEPPOL_30" }, - "errors": [ + "validation_results": [ { - "rule_id": "BR-CO-15", - "rule_location": "/*:CrossIndustryInvoice[namespace-uri()='urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100'][1]", - "rule_severity": "FATAL", - "rule_messages": [ - "[BR-CO-15]-Invoice total amount with VAT (BT-112) = Invoice total amount without VAT (BT-109) + Invoice total VAT amount (BT-110)." + "name": "EN16931", + "version": "1.3.13", + "warnings": [], + "errors": [ + { + "rule_id": "BR-CL-14", + "rule_location": "/*:Invoice[namespace-uri()='urn:oasis:names:specification:ubl:schema:xsd:Invoice-2'][1]/*:AccountingSupplierParty[namespace-uri()='urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2'][1]/*:Party[namespace-uri()='urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2'][1]/*:PostalAddress[namespace-uri()='urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2'][1]/*:Country[namespace-uri()='urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2'][1]/*:IdentificationCode[namespace-uri()='urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2'][1]", + "rule_severity": "FATAL", + "rule_messages": [ + "[BR-CL-14]-Country codes in an invoice MUST be coded using ISO code list 3166-1" + ] + } ] }, { - "rule_id": "BR-CL-04", - "rule_location": "/*:CrossIndustryInvoice[namespace-uri()='urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100'][1]/*:SupplyChainTradeTransaction[namespace-uri()='urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100'][1]/*:ApplicableHeaderTradeSettlement[namespace-uri()='urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100'][1]/*:InvoiceCurrencyCode[namespace-uri()='urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100'][1]", - "rule_severity": "FATAL", - "rule_messages": [ - "[BR-CL-04]-Invoice currency code MUST be coded using ISO code list 4217 alpha-3" - ] + "name": "Peppol BIS", + "version": "3.0", + "warnings": [], + "errors": [] } ], - "warnings": [], "is_valid": false } ``` From 9d5633a9b0525c14e060ab55460695a3571da528 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20N=C3=B6hles?= Date: Thu, 30 Jan 2025 13:32:52 +0100 Subject: [PATCH 5/7] Fix tests --- .../easybill/ValidationControllerTest.java | 51 ++++++++++++------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/src/test/java/io/github/easybill/ValidationControllerTest.java b/src/test/java/io/github/easybill/ValidationControllerTest.java index 66011aa..785dbb3 100644 --- a/src/test/java/io/github/easybill/ValidationControllerTest.java +++ b/src/test/java/io/github/easybill/ValidationControllerTest.java @@ -72,7 +72,7 @@ void testValidationEndpointWithPayloadIncludingCharsInProlog() .statusCode(200) .contentType(ContentType.JSON) .body("is_valid", equalTo(false)) - .body("errors", not(empty())); + .body("validation_results[0].errors", not(empty())); } static Stream providerValuesForDifferentEncodings() { @@ -96,8 +96,7 @@ void testValidationEndpointWithDifferentEncodings( .then() .statusCode(200) .contentType(ContentType.JSON) - .body("is_valid", equalTo(true)) - .body("errors", empty()); + .body("is_valid", equalTo(true)); } @ParameterizedTest @@ -144,7 +143,7 @@ void testValidEN16931Documents(@NonNull String fixtureFileName) equalTo(XmlProfileType.EN16931.name()) ) .body("is_valid", equalTo(true)) - .body("errors", empty()); + .body("validation_results[0].errors", empty()); } @ParameterizedTest @@ -164,7 +163,7 @@ void testInvalidEN16931Documents(@NonNull String fixtureFileName) equalTo(XmlProfileType.EN16931.name()) ) .body("is_valid", equalTo(false)) - .body("errors", not(empty())); + .body("validation_results[0].errors", not(empty())); } @ParameterizedTest @@ -191,16 +190,13 @@ void testValidFacturXDocuments(@NonNull String fixtureFileName) .statusCode(200) .contentType(ContentType.JSON) .body("meta.xml_syntax_type", equalTo(XMLSyntaxType.CII.name())) - .body("is_valid", equalTo(true)) - .body("errors", empty()); + .body("is_valid", equalTo(true)); } @ParameterizedTest @ValueSource( strings = { - "FacturX/FacturX_001.xml", "FacturX/FacturX_003.xml", - "FacturX/FacturX_008.xml", "FacturX/FacturX_014.xml", "FacturX/FacturX_015.xml", "FacturX/FacturX_018.xml", @@ -221,8 +217,29 @@ void testValidFacturXExtendedDocuments(@NonNull String fixtureFileName) "meta.xml_profile_type", equalTo(XmlProfileType.FACTURX_EXTENDED.name()) ) - .body("is_valid", equalTo(true)) - .body("errors", empty()); + .body("is_valid", equalTo(true)); + } + + @ParameterizedTest + @ValueSource( + strings = { "FacturX/FacturX_001.xml", "FacturX/FacturX_008.xml" } + ) + void testInvalidFacturXExtendedDocuments(@NonNull String fixtureFileName) + throws IOException { + given() + .body(loadFixtureFileAsStream(fixtureFileName)) + .contentType(ContentType.XML) + .when() + .post("/validation") + .then() + .statusCode(200) + .contentType(ContentType.JSON) + .body("meta.xml_syntax_type", equalTo(XMLSyntaxType.CII.name())) + .body( + "meta.xml_profile_type", + equalTo(XmlProfileType.FACTURX_EXTENDED.name()) + ) + .body("is_valid", equalTo(false)); } @ParameterizedTest @@ -285,8 +302,7 @@ void testValidPeppolDocuments(@NonNull String fixtureFileName) "meta.xml_profile_type", equalTo(XmlProfileType.PEPPOL_30.name()) ) - .body("is_valid", equalTo(true)) - .body("errors", empty()); + .body("is_valid", equalTo(true)); } @ParameterizedTest @@ -312,8 +328,7 @@ void testInvalidPeppolDocuments(@NonNull String fixtureFileName) "meta.xml_profile_type", equalTo(XmlProfileType.PEPPOL_30.name()) ) - .body("is_valid", equalTo(false)) - .body("errors", not(empty())); + .body("is_valid", equalTo(false)); } @ParameterizedTest @@ -339,8 +354,7 @@ void testValidXRechnungDocuments(@NonNull String fixtureFileName) "meta.xml_profile_type", equalTo(XmlProfileType.XRECHNUNG_30.name()) ) - .body("is_valid", equalTo(true)) - .body("errors", empty()); + .body("is_valid", equalTo(true)); } @ParameterizedTest @@ -383,8 +397,7 @@ void testValidationEndpointWithInvalidPayload( .then() .statusCode(200) .contentType(ContentType.JSON) - .body("is_valid", equalTo(false)) - .body("errors", not(empty())); + .body("is_valid", equalTo(false)); } InputStream loadFixtureFileAsStream(@NonNull String fixtureFileName) From 07064299d1c028efc9f72bfd04c14ace857d463d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20N=C3=B6hles?= Date: Thu, 30 Jan 2025 13:33:33 +0100 Subject: [PATCH 6/7] Increase app version --- src/main/resources/application.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 3c34311..23a8978 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,4 +1,4 @@ -app.version=0.1.0 +app.version=0.2.0 app.exceptions.bugsnag-api-key=${BUGSNAG_API_KEY} quarkus.smallrye-openapi.info-title=E-Invoice Validator API From b08fd3cfd852688222f8e5630b23f644244fa922 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20N=C3=B6hles?= Date: Thu, 30 Jan 2025 13:38:39 +0100 Subject: [PATCH 7/7] Fix spotbugs --- .../io/github/easybill/Dtos/ValidationResult.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/java/io/github/easybill/Dtos/ValidationResult.java b/src/main/java/io/github/easybill/Dtos/ValidationResult.java index ac755a0..d74941f 100644 --- a/src/main/java/io/github/easybill/Dtos/ValidationResult.java +++ b/src/main/java/io/github/easybill/Dtos/ValidationResult.java @@ -4,6 +4,7 @@ import io.github.easybill.Dtos.ValidatorResults.ValidatorResult; import io.github.easybill.Enums.XmlProfileType; import java.util.Arrays; +import java.util.Collections; import java.util.List; import org.checkerframework.checker.nullness.qual.NonNull; @@ -13,6 +14,17 @@ public record ValidationResult( @NonNull List<@NonNull ValidatorResult> validationResults ) { + public ValidationResult( + @NonNull ValidationResultMetaData meta, + @JsonProperty( + "validation_results" + ) @NonNull List<@NonNull ValidatorResult> validationResults + ) { + this.meta = meta; + this.validationResults = + Collections.unmodifiableList(validationResults); + } + @JsonProperty("is_valid") public boolean isValid() { return validationResults