Skip to content

Commit

Permalink
Merge pull request #4 from easybill/v0.2.1.1
Browse files Browse the repository at this point in the history
V0.2.1
  • Loading branch information
BolZer authored Oct 28, 2024
2 parents 717139e + be618cb commit a39b487
Show file tree
Hide file tree
Showing 9 changed files with 605 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import io.github.easybill.Contracts.IValidationService;
import io.github.easybill.Dtos.ValidationResult;
import io.github.easybill.Exceptions.InvalidXmlException;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
Expand Down Expand Up @@ -42,23 +41,17 @@ public ValidationController(IValidationService validationService) {
public RestResponse<@NonNull ValidationResult> validation(
InputStream xmlInputStream
) throws Exception {
try {
ValidationResult result = validationService.validateXml(
xmlInputStream
);

if (result.isValid()) {
return RestResponse.ResponseBuilder
.ok(result, MediaType.APPLICATION_JSON)
.build();
}
ValidationResult result = validationService.validateXml(xmlInputStream);

if (result.isValid()) {
return RestResponse.ResponseBuilder
.create(RestResponse.Status.BAD_REQUEST, result)
.type(MediaType.APPLICATION_JSON)
.ok(result, MediaType.APPLICATION_JSON)
.build();
} catch (InvalidXmlException exception) {
return RestResponse.status(RestResponse.Status.BAD_REQUEST);
}

return RestResponse.ResponseBuilder
.create(RestResponse.Status.BAD_REQUEST, result)
.type(MediaType.APPLICATION_JSON)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package io.github.easybill.Exceptions;

public class InvalidXmlException extends RuntimeException {
public class InvalidXmlException extends ValidatorException {

public InvalidXmlException() {
super();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.github.easybill.Exceptions;

public class ParsingException extends ValidatorException {

public ParsingException(Throwable cause) {
super("could not parse exception", cause);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.github.easybill.Exceptions;

import org.checkerframework.checker.nullness.qual.NonNull;

abstract class ValidatorException extends RuntimeException {

public ValidatorException() {
super();
}

public ValidatorException(Throwable cause) {
super(cause);
}

public ValidatorException(@NonNull String message, Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package io.github.easybill.Interceptors;

import io.github.easybill.Exceptions.InvalidXmlException;
import io.github.easybill.Exceptions.ParsingException;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper;
Expand All @@ -19,6 +21,16 @@ public Response toResponse(Throwable exception) {
return ((WebApplicationException) exception).getResponse();
}

if (exception instanceof InvalidXmlException) {
return Response.status(422, "Unprocessable Content").build();
}

if (exception instanceof ParsingException) {
return Response
.status(422, "Unprocessable Content - Parsing Error")
.build();
}

LOGGER.error("Encountered an exception:", exception);

return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
Expand Down
56 changes: 43 additions & 13 deletions src/main/java/io/github/easybill/Services/ValidationService.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import io.github.easybill.Dtos.ValidationResultMetaData;
import io.github.easybill.Enums.XMLSyntaxType;
import io.github.easybill.Exceptions.InvalidXmlException;
import io.github.easybill.Exceptions.ParsingException;
import jakarta.inject.Singleton;
import java.io.InputStream;
import java.nio.charset.Charset;
Expand Down Expand Up @@ -72,10 +73,15 @@ public final class ValidationService implements IValidationService {
throw new InvalidXmlException();
}

xml = removeBOM(xml);

var xmlSyntaxType = determineXmlSyntax(xml)
.orElseThrow(InvalidXmlException::new);

var report = innerValidateSchematron(xmlSyntaxType, bytesFromSteam)
var report = innerValidateSchematron(
xmlSyntaxType,
xml.getBytes(charset)
)
.orElseThrow(RuntimeException::new);

return new ValidationResult(
Expand Down Expand Up @@ -171,21 +177,45 @@ private boolean checkIfUblXml(@NonNull CharSequence payload) {
.find();
}

private @NonNull String removeBOM(@NonNull String $payload) {
String UTF8_BOM = "\uFEFF";
String UTF16LE_BOM = "\uFFFE";
String UTF16BE_BOM = "\uFEFF";

if ($payload.isEmpty()) {
return $payload;
}

if (
$payload.startsWith(UTF8_BOM) ||
$payload.startsWith(UTF16LE_BOM) ||
$payload.startsWith(UTF16BE_BOM)
) {
return $payload.substring(1);
}

return $payload;
}

private Optional<SchematronOutputType> innerValidateSchematron(
@NonNull XMLSyntaxType xmlSyntaxType,
byte[] bytes
) throws Exception {
return switch (xmlSyntaxType) {
case CII -> Optional.ofNullable(
ciiSchematron.applySchematronValidationToSVRL(
new ByteArrayWrapper(bytes, false)
)
);
case UBL -> Optional.ofNullable(
ublSchematron.applySchematronValidationToSVRL(
new ByteArrayWrapper(bytes, false)
)
);
};
try {
return switch (xmlSyntaxType) {
case CII -> Optional.ofNullable(
ciiSchematron.applySchematronValidationToSVRL(
new ByteArrayWrapper(bytes, false)
)
);
case UBL -> Optional.ofNullable(
ublSchematron.applySchematronValidationToSVRL(
new ByteArrayWrapper(bytes, false)
)
);
};
} catch (IllegalArgumentException exception) {
throw new ParsingException(exception);
}
}
}
28 changes: 27 additions & 1 deletion src/test/java/io/github/easybill/ValidationControllerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,33 @@ void testValidationEndpointWithEmptyPayload() throws IOException {
.when()
.post("/validation")
.then()
.statusCode(400);
.statusCode(422);
}

@Test
void testValidationEndpointWithPayloadIncludingBOM() throws IOException {
given()
.body(loadFixtureFileAsStream("CII/EN16931_Einfach_BOM.xml"))
.contentType(ContentType.XML)
.when()
.post("/validation")
.then()
.statusCode(200)
.contentType(ContentType.JSON)
.body("is_valid", equalTo(true))
.body("errors", empty());
}

@Test
void testValidationEndpointWithPayloadIncludingCharsInProlog()
throws IOException {
given()
.body(loadFixtureFileAsStream("Invalid/EN16931_Einfach_BOM.xml"))
.contentType(ContentType.XML)
.when()
.post("/validation")
.then()
.statusCode(422);
}

@ParameterizedTest
Expand Down
Loading

0 comments on commit a39b487

Please sign in to comment.