Compare commits

...

11 Commits

Author SHA1 Message Date
d5c2a5f813 [FIX] correct some generic code generation 2025-03-17 19:48:30 +01:00
85ac72648f [FIX] not ready dependency 2025-03-16 11:48:33 +01:00
1281415c48 [DEV] integrate hybernate validator 2025-03-16 11:24:16 +01:00
dependabot[bot]
a4521853c3 [DEV-OPS] (dependabot) Bump com.twelvemonkeys.imageio:imageio-webp
Bumps com.twelvemonkeys.imageio:imageio-webp from 3.11.0 to 3.12.0.

---
updated-dependencies:
- dependency-name: com.twelvemonkeys.imageio:imageio-webp
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-15 08:51:19 +01:00
dependabot[bot]
abe2dd9480 [DEV-OPS] (dependabot) Bump com.nimbusds:nimbus-jose-jwt
Bumps [com.nimbusds:nimbus-jose-jwt](https://bitbucket.org/connect2id/nimbus-jose-jwt) from 9.41.1 to 10.0.2.
- [Changelog](https://bitbucket.org/connect2id/nimbus-jose-jwt/src/master/CHANGELOG.txt)
- [Commits](https://bitbucket.org/connect2id/nimbus-jose-jwt/branches/compare/10.0.2..9.41.1)

---
updated-dependencies:
- dependency-name: com.nimbusds:nimbus-jose-jwt
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-15 08:36:46 +01:00
dependabot[bot]
b7af0b4575 [DEV-OPS] (dependabot) Bump org.hibernate.validator:hibernate-validator
Bumps [org.hibernate.validator:hibernate-validator](https://github.com/hibernate/hibernate-validator) from 8.0.1.Final to 8.0.2.Final.
- [Changelog](https://github.com/hibernate/hibernate-validator/blob/8.0.2.Final/changelog.txt)
- [Commits](https://github.com/hibernate/hibernate-validator/compare/8.0.1.Final...8.0.2.Final)

---
updated-dependencies:
- dependency-name: org.hibernate.validator:hibernate-validator
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-15 08:36:43 +01:00
dependabot[bot]
5412eadf2e [DEV-OPS] (dependabot) Bump org.apache.maven.plugins:maven-checkstyle-plugin
Bumps [org.apache.maven.plugins:maven-checkstyle-plugin](https://github.com/apache/maven-checkstyle-plugin) from 3.5.0 to 3.6.0.
- [Commits](https://github.com/apache/maven-checkstyle-plugin/compare/maven-checkstyle-plugin-3.5.0...maven-checkstyle-plugin-3.6.0)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-checkstyle-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-15 08:36:41 +01:00
dependabot[bot]
d5e2e0f5b3 [DEV-OPS] (dependabot) Bump org.glassfish.jersey:jersey-bom
Bumps org.glassfish.jersey:jersey-bom from 3.1.5 to 3.1.10.

---
updated-dependencies:
- dependency-name: org.glassfish.jersey:jersey-bom
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-15 08:36:38 +01:00
28ab6c49d4 [FIX] update PR title 2025-03-15 08:30:14 +01:00
260a3abd13 [FEAT] update dependabot 2025-03-15 08:25:09 +01:00
895e8c2b37 [FEAT] update versions 2025-03-15 08:18:14 +01:00
27 changed files with 709 additions and 63 deletions

View File

@ -30,15 +30,6 @@
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="target/generated-test-sources/test-annotations">
<attributes>
<attribute name="test" value="true"/>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="ignore_optional_problems" value="true"/>
<attribute name="m2e-apt" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" path="target/generated-sources/annotations">
<attributes>
<attribute name="optional" value="true"/>

View File

@ -1,12 +1,24 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
---
version: 2
updates:
- package-ecosystem: "" # See documentation for possible values
directory: "/" # Location of package manifests
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
interval: "daily"
time: "05:00"
timezone: "Europe/Paris"
commit-message:
prefix: "[DEV-OPS] (dependabot) "
assignees:
- HeeroYui
- package-ecosystem: "maven"
directory: "/"
schedule:
interval: "daily"
time: "06:00"
timezone: "Europe/Paris"
commit-message:
prefix: "[DEV-OPS] (dependabot) "
assignees:
- HeeroYui

View File

@ -9,6 +9,8 @@ on:
- synchronize
- ready_for_review
- reopened
- labeled
- unlabeled
jobs:
check-title:
@ -17,7 +19,7 @@ jobs:
- name: "Check title"
uses: Slashgear/action-check-pr-title@v4.3.0
with:
regexp: "\\[(API,)?(API|DEV-OPS|DOC|FEAT|FIX|FIX\\-CI|STYLE)\\]( \\([A-Za-z0-9.\\-]+\\))? [A-Za-z0-9 ,.'\\-!]+$"
regexp: "\\[(API,)?(API|DEV-OPS|DOC|FEAT|FIX|FIX\\-CI|STYLE)\\]( \\([A-Za-z0-9.\\-/_]+\\))? [A-Za-z0-9: ,.'\\-!/_]+$"
helpMessage: |
Title of the PR MUST respect format: "[{TYPE}] clear description without typos in english" with {TYPE}:
* [API] Change API that permit to access on the application (un-compatibility only). This one can specifically added with [API,{TYPE}]

59
pom.xml
View File

@ -4,16 +4,6 @@
<groupId>kangaroo-and-rabbit</groupId>
<artifactId>archidata</artifactId>
<version>0.24.1-SNAPSHOT</version>
<properties>
<java.version>21</java.version>
<maven.compiler.version>3.1</maven.compiler.version>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<maven.dependency.version>3.1.1</maven.dependency.version>
<jersey.version>3.1.5</jersey.version>
<jaxb.version>2.3.1</jaxb.version>
<istack.version>4.1.1</istack.version>
</properties>
<repositories>
<repository>
<id>gitea</id>
@ -35,7 +25,7 @@
<dependency>
<groupId>org.glassfish.jersey</groupId>
<artifactId>jersey-bom</artifactId>
<version>${jersey.version}</version>
<version>3.1.10</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@ -58,7 +48,7 @@
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-webp</artifactId>
<version>3.11.0</version>
<version>3.12.0</version>
</dependency>
<!-- Decode JPEG image -->
<dependency>
@ -95,10 +85,19 @@
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-grizzly2-http</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-bean-validation</artifactId>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>${jaxb.version}</version>
<version>2.4.0-b180830.0359</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>4.0.0-M4</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
@ -110,15 +109,10 @@
<artifactId>jakarta.ws.rs-api</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>${jaxb.version}</version>
</dependency>
<dependency>
<groupId>com.sun.istack</groupId>
<artifactId>istack-commons-runtime</artifactId>
<version>${istack.version}</version>
<version>4.1.2</version>
</dependency>
<!-- continu to be needed ??? -->
<dependency>
@ -168,7 +162,7 @@
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>9.41.1</version>
<version>10.0.2</version>
</dependency>
<dependency>
<groupId>jakarta.persistence</groupId>
@ -198,13 +192,13 @@
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-sync</artifactId>
<version>4.3.0</version>
<version>5.1.4</version>
</dependency>
<!-- Bean Validation (JSR 303 / 380) -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>7.0.0.Final</version>
<version>8.0.2.Final</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
@ -236,7 +230,7 @@
<dependency>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.5.0</version>
<version>3.6.0</version>
</dependency>
</dependencies>
<build>
@ -256,18 +250,17 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven.compiler.version}</version>
<version>3.14.0</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<!--<encoding>${project.build.sourceEncoding}</encoding>-->
<source>21</source>
<target>21</target>
</configuration>
</plugin>
<!-- Create the source bundle -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.3.0</version>
<version>3.3.1</version>
<executions>
<execution>
<id>attach-sources</id>
@ -295,7 +288,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
<version>3.2.5</version>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
@ -314,7 +307,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.2.0</version>
<version>3.3.0</version>
<configuration>
<show>private</show>
<nohelp>true</nohelp>
@ -324,7 +317,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.3.1</version>
<version>3.6.0</version>
<configuration>
<configLocation>CheckStyle.xml</configLocation>
<consoleOutput>true</consoleOutput>
@ -336,7 +329,7 @@
<plugin>
<groupId>net.revelc.code.formatter</groupId>
<artifactId>formatter-maven-plugin</artifactId>
<version>2.23.0</version>
<version>2.24.1</version>
<configuration>
<encoding>UTF-8</encoding>
<lineEnding>LF</lineEnding>
@ -385,7 +378,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.2.0</version>
<version>3.3.0</version>
<configuration>
<show>public</show>
</configuration>

View File

@ -5,8 +5,18 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ ElementType.TYPE, ElementType.FIELD })
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
@Constraint(validatedBy = CheckForeignKeyValidator.class)
@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckForeignKey {
Class<?> target();
String message() default "Foreign-key does not exist in the DB";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@ -0,0 +1,43 @@
package org.kar.archidata.annotation.checker;
import java.util.Collection;
import org.kar.archidata.dataAccess.DataAccess;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
public class CheckForeignKeyValidator implements ConstraintValidator<CheckForeignKey, Object> {
Class<?> target = null;
@Override
public void initialize(final CheckForeignKey annotation) {
this.target = annotation.target();
}
@Override
public boolean isValid(final Object value, final ConstraintValidatorContext context) {
if (value != null) {
return true;
}
if (value instanceof final Collection<?> tmpCollection) {
final Object[] elements = tmpCollection.toArray();
for (final Object element : elements) {
if (element == null) {
continue;
}
try {
final long count = DataAccess.count(this.target, element);
if (count != 1) {
return false;
}
} catch (final Exception e) {
// TODO ...
return false;
}
}
}
return true;
}
}

View File

@ -5,8 +5,16 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
@Constraint(validatedBy = CollectionItemNotNullValidator.class)
@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface CollectionItemNotNull {
String message() default "Collection can not contain NULL item";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@ -0,0 +1,31 @@
package org.kar.archidata.annotation.checker;
import java.util.Collection;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
public class CollectionItemNotNullValidator implements ConstraintValidator<CollectionItemNotNull, Object> {
@Override
public void initialize(final CollectionItemNotNull annotation) {
// nothing to do...
}
@Override
public boolean isValid(final Object value, final ConstraintValidatorContext context) {
if (value == null) {
return true;
}
if (value instanceof final Collection<?> tmpCollection) {
final Object[] elements = tmpCollection.toArray();
for (final Object element : elements) {
if (element == null) {
return false;
//throw new InputException(baseName + fieldName + '[' + iii + ']', "Collection can not contain NULL item");
}
}
}
return true;
}
}

View File

@ -5,8 +5,17 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
@Constraint(validatedBy = CollectionItemUniqueValidator.class)
@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface CollectionItemUnique {
String message() default "Cannot insert multiple times the same elements";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@ -0,0 +1,30 @@
package org.kar.archidata.annotation.checker;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
public class CollectionItemUniqueValidator implements ConstraintValidator<CollectionItemUnique, Object> {
@Override
public void initialize(final CollectionItemUnique annotation) {
// nothing to do...
}
@Override
public boolean isValid(final Object value, final ConstraintValidatorContext context) {
if (value == null) {
return true;
}
if (value instanceof final Collection<?> tmpCollection) {
final Set<Object> uniqueValues = new HashSet<>(tmpCollection);
if (uniqueValues.size() != tmpCollection.size()) {
return false;
}
}
return true;
}
}

View File

@ -5,8 +5,17 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
@Constraint(validatedBy = CollectionNotEmptyValidator.class)
@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface CollectionNotEmpty {
String message() default "Collection can not be empty";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@ -0,0 +1,27 @@
package org.kar.archidata.annotation.checker;
import java.util.Collection;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
public class CollectionNotEmptyValidator implements ConstraintValidator<CollectionNotEmpty, Object> {
@Override
public void initialize(final CollectionNotEmpty annotation) {
// nothing to do...
}
@Override
public boolean isValid(final Object value, final ConstraintValidatorContext context) {
if (value == null) {
return true;
}
if (value instanceof final Collection<?> tmpCollection) {
if (tmpCollection.isEmpty()) {
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,20 @@
package org.kar.archidata.annotation.checker;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
@Constraint(validatedBy = ReadOnlyFieldValidator.class)
@Target({ ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface ReadOnlyField {
String message() default "Field can not be set, it is a read-only field.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@ -0,0 +1,20 @@
package org.kar.archidata.annotation.checker;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
public class ReadOnlyFieldValidator implements ConstraintValidator<ReadOnlyField, Object> {
@Override
public void initialize(final ReadOnlyField annotation) {
// nothing to do...
}
@Override
public boolean isValid(final Object value, final ConstraintValidatorContext context) {
if (value != null) {
return false;
}
return true;
}
}

View File

@ -1,5 +1,11 @@
package org.kar.archidata.catcher;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -20,7 +26,22 @@ public class ConstraintViolationExceptionCatcher implements ExceptionMapper<Cons
}
private RestErrorResponse build(final ConstraintViolationException exception) {
return new RestErrorResponse(Response.Status.BAD_REQUEST, "Constraint Violation", exception.getMessage());
final List<RestInputError> inputError = new ArrayList<>();
for (final var cv : exception.getConstraintViolations()) {
if (cv == null) {
continue;
}
inputError.add(new RestInputError(cv.getPropertyPath(), cv.getMessage()));
}
Collections.sort(inputError, Comparator.comparing(RestInputError::getFullPath));
String errorType = "Multiple error on input";
if (inputError.size() == 0) {
errorType = "Constraint Violation";
} else if (inputError.size() == 1) {
errorType = "Error on input='" + inputError.get(0).path + "'";
}
return new RestErrorResponse(Response.Status.BAD_REQUEST, Instant.now().toString(), errorType,
exception.getMessage(), inputError);
}
}

View File

@ -1,10 +1,12 @@
package org.kar.archidata.catcher;
import java.time.Instant;
import java.util.List;
import org.bson.types.ObjectId;
import org.kar.archidata.annotation.apiGenerator.ApiGenerationMode;
import jakarta.annotation.Nullable;
import jakarta.persistence.Column;
import jakarta.validation.constraints.NotNull;
import jakarta.ws.rs.core.Response;
@ -27,6 +29,19 @@ public class RestErrorResponse {
@Column(length = 0)
final public String statusMessage;
@Nullable
final public List<RestInputError> inputError;
public RestErrorResponse(final Response.Status status, final String time, final String error, final String message,
final List<RestInputError> inputError) {
this.time = time;
this.name = error;
this.message = message;
this.status = status.getStatusCode();
this.statusMessage = status.getReasonPhrase();
this.inputError = inputError;
}
public RestErrorResponse(final Response.Status status, final String time, final String error,
final String message) {
this.time = time;
@ -34,6 +49,7 @@ public class RestErrorResponse {
this.message = message;
this.status = status.getStatusCode();
this.statusMessage = status.getReasonPhrase();
this.inputError = null;
}
public RestErrorResponse(final Response.Status status, final String error, final String message) {
@ -42,6 +58,7 @@ public class RestErrorResponse {
this.message = message;
this.status = status.getStatusCode();
this.statusMessage = status.getReasonPhrase();
this.inputError = null;
}
public RestErrorResponse(final Response.Status status) {
@ -50,6 +67,7 @@ public class RestErrorResponse {
this.time = Instant.now().toString();
this.status = status.getStatusCode();
this.statusMessage = status.getReasonPhrase();
this.inputError = null;
}
}

View File

@ -0,0 +1,50 @@
package org.kar.archidata.catcher;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import jakarta.persistence.Column;
import jakarta.validation.Path;
import jakarta.validation.constraints.NotNull;
public class RestInputError {
private static Pattern PATTERN = Pattern.compile("^([^.]+)\\.([^.]+)(\\.(.*))?");
@Column(length = 0)
public String argument;
@Column(length = 0)
public String path;
@NotNull
@Column(length = 0)
public String message;
@Override
public String toString() {
return "RestInputError [argument=" + this.argument + ", path=" + this.path + ", message=" + this.message + "]";
}
public RestInputError() {}
public RestInputError(final Path path, final String message) {
final Matcher matcher = PATTERN.matcher(path.toString());
if (matcher.find()) {
//String firstPart = matcher.group(1); this is the request base element ==> not needed
this.argument = matcher.group(2);
this.path = matcher.group(4);
} else {
this.path = path.toString();
}
this.message = message;
}
public RestInputError(final String path, final String message) {
this.path = path;
this.message = message;
}
String getFullPath() {
if (this.path == null) {
return this.argument;
}
return this.argument + "." + this.path;
}
}

View File

@ -1,6 +1,9 @@
package org.kar.archidata.exception;
import java.util.List;
import org.bson.types.ObjectId;
import org.kar.archidata.catcher.RestInputError;
public class RESTErrorResponseException extends Exception {
private static final long serialVersionUID = 1L;
@ -10,6 +13,7 @@ public class RESTErrorResponseException extends Exception {
public String message;
public int status;
public String statusMessage;
public List<RestInputError> inputError;
public RESTErrorResponseException() {
this.oid = new ObjectId();
@ -18,16 +22,18 @@ public class RESTErrorResponseException extends Exception {
this.message = null;
this.status = 0;
this.statusMessage = null;
this.inputError = null;
}
public RESTErrorResponseException(final ObjectId oid, final String time, final String name, final String message,
final int status, final String statusMessage) {
final int status, final String statusMessage, final List<RestInputError> inputError) {
this.oid = oid;
this.time = time;
this.name = name;
this.message = message;
this.status = status;
this.statusMessage = statusMessage;
this.inputError = inputError;
}
@Override

View File

@ -20,6 +20,7 @@ import java.util.UUID;
import org.kar.archidata.annotation.DataIfNotExists;
import org.kar.archidata.annotation.DataJson;
import org.kar.archidata.annotation.apiGenerator.ApiGenerationMode;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonInclude;
@ -36,6 +37,7 @@ import jakarta.ws.rs.DefaultValue;
@Table(name = "user")
@DataIfNotExists
@JsonInclude(JsonInclude.Include.NON_NULL)
@ApiGenerationMode(create = true, update = true)
public class User extends GenericDataSoftDelete {
@NotNull
@Column(length = 128)

View File

@ -17,6 +17,7 @@ import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
import jakarta.ws.rs.core.HttpHeaders;
@ -170,14 +171,21 @@ public class RESTApi {
final RESTErrorResponseException out = this.mapper.readValue(httpResponse.body(),
RESTErrorResponseException.class);
throw out;
} catch (final InvalidDefinitionException ex) {
ex.printStackTrace();
LOGGER.error("body: {}", httpResponse.body());
throw new IOException("RestAPI Fail to parse the error " + ex.getClass().getName() + " ["
+ httpResponse.statusCode() + "] " + httpResponse.body());
} catch (final MismatchedInputException ex) {
throw new IOException(
"Fail to get the data [" + httpResponse.statusCode() + "] " + httpResponse.body());
ex.printStackTrace();
LOGGER.error("body: {}", httpResponse.body());
throw new IOException("RestAPI Fail to parse the error " + ex.getClass().getName() + " ["
+ httpResponse.statusCode() + "] " + httpResponse.body());
} catch (final JsonParseException ex) {
ex.printStackTrace();
LOGGER.error("body: {}", httpResponse.body());
throw new IOException(
"Fail to get the ERROR data [" + httpResponse.statusCode() + "] " + httpResponse.body());
throw new IOException("RestAPI Fail to parse the error " + ex.getClass().getName() + " ["
+ httpResponse.statusCode() + "] " + httpResponse.body());
}
}
if (clazz == Void.class || clazz == void.class) {

View File

@ -30,8 +30,6 @@ public class TestTime {
static WebLauncherTest webInterface = null;
static RESTApi api = null;
private static Long idTest = 0L;
@BeforeAll
public static void configureWebServer() throws Exception {
ConfigureDb.configure();

View File

@ -0,0 +1,95 @@
package test.kar.archidata.hybernateValidator;
import java.util.ArrayList;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.extension.ExtendWith;
import org.kar.archidata.exception.RESTErrorResponseException;
import org.kar.archidata.tools.ConfigBaseVariable;
import org.kar.archidata.tools.RESTApi;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import test.kar.archidata.ConfigureDb;
import test.kar.archidata.StepwiseExtension;
import test.kar.archidata.apiExtern.Common;
import test.kar.archidata.hybernateValidator.model.ValidatorModel;
import test.kar.archidata.hybernateValidator.model.ValidatorSubModel;
@ExtendWith(StepwiseExtension.class)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class TestValidator {
private final static Logger LOGGER = LoggerFactory.getLogger(TestValidator.class);
public final static String ENDPOINT_NAME = "TestResourceValidator";
static WebLauncherTest webInterface = null;
static RESTApi api = null;
@BeforeAll
public static void configureWebServer() throws Exception {
ConfigureDb.configure();
LOGGER.info("configure server ...");
webInterface = new WebLauncherTest();
LOGGER.info("Clean previous table");
LOGGER.info("Start REST (BEGIN)");
webInterface.process();
LOGGER.info("Start REST (DONE)");
api = new RESTApi(ConfigBaseVariable.apiAdress);
api.setToken(Common.ADMIN_TOKEN);
}
@AfterAll
public static void stopWebServer() throws Exception {
LOGGER.info("Kill the web server");
webInterface.stop();
webInterface = null;
ConfigureDb.clear();
}
@Order(2)
@Test
public void DetectGenericError() throws Exception {
final ValidatorModel data = new ValidatorModel();
data.value = "plop";
data.data = "klsdfsdfsdfsdfj";
data.multipleElement = new ArrayList<>();
ValidatorSubModel tmp = new ValidatorSubModel();
tmp.data = "lkmkmlkmlklm";
data.multipleElement.add(tmp);
tmp = new ValidatorSubModel();
tmp.data = "1";
data.multipleElement.add(tmp);
data.subElement = new ValidatorSubModel();
data.subElement.data = "k";
final RESTErrorResponseException exception = Assertions.assertThrows(RESTErrorResponseException.class,
() -> api.post(void.class, TestValidator.ENDPOINT_NAME + "?queryParametersName=2", data));
Assertions.assertNotNull(exception);
LOGGER.debug("error on input:{}", exception);
Assertions.assertNull(exception.getMessage());
Assertions.assertNotNull(exception.inputError);
Assertions.assertEquals(5, exception.inputError.size());
Assertions.assertEquals("arg0", exception.inputError.get(0).argument);
Assertions.assertEquals(null, exception.inputError.get(0).path);
Assertions.assertEquals("must be greater than or equal to 5", exception.inputError.get(0).message);
Assertions.assertEquals("arg1", exception.inputError.get(1).argument);
Assertions.assertEquals("data", exception.inputError.get(1).path);
Assertions.assertEquals("size must be between 0 and 5", exception.inputError.get(1).message);
Assertions.assertEquals("arg1", exception.inputError.get(2).argument);
Assertions.assertEquals("multipleElement[1].data", exception.inputError.get(2).path);
Assertions.assertEquals("size must be between 2 and 2147483647", exception.inputError.get(2).message);
Assertions.assertEquals("arg1", exception.inputError.get(3).argument);
Assertions.assertEquals("subElement.data", exception.inputError.get(3).path);
Assertions.assertEquals("size must be between 2 and 2147483647", exception.inputError.get(3).message);
Assertions.assertEquals("arg1", exception.inputError.get(4).argument);
Assertions.assertEquals("value", exception.inputError.get(4).path);
Assertions.assertEquals("Field can not be set, it is a read-only field.", exception.inputError.get(4).message);
}
}

View File

@ -0,0 +1,163 @@
package test.kar.archidata.hybernateValidator;
import java.net.URI;
import java.util.Iterator;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.ImageWriter;
import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.validation.ValidationFeature;
import org.kar.archidata.UpdateJwtPublicKey;
import org.kar.archidata.catcher.GenericCatcher;
import org.kar.archidata.db.DbConfig;
import org.kar.archidata.exception.DataAccessException;
import org.kar.archidata.filter.CORSFilter;
import org.kar.archidata.filter.OptionFilter;
import org.kar.archidata.migration.MigrationEngine;
import org.kar.archidata.tools.ConfigBaseVariable;
import org.kar.archidata.tools.ContextGenericTools;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.ws.rs.core.UriBuilder;
import test.kar.archidata.hybernateValidator.resource.TestResourceValidator;
public class WebLauncher {
final static Logger LOGGER = LoggerFactory.getLogger(WebLauncher.class);
protected UpdateJwtPublicKey keyUpdater = null;
protected HttpServer server = null;
public WebLauncher() {}
private static URI getBaseURI() {
return UriBuilder.fromUri(ConfigBaseVariable.getlocalAddress()).build();
}
public void migrateDB() throws Exception {
WebLauncher.LOGGER.info("Create migration engine");
final MigrationEngine migrationEngine = new MigrationEngine();
WebLauncher.LOGGER.info("Add initialization");
//migrationEngine.setInit(new Initialization());
WebLauncher.LOGGER.info("Add migration since last version");
//migrationEngine.add(new Migration20231126());
WebLauncher.LOGGER.info("Migrate the DB [START]");
migrationEngine.migrateWaitAdmin(new DbConfig());
WebLauncher.LOGGER.info("Migrate the DB [STOP]");
}
public static void main(final String[] args) throws Exception {
WebLauncher.LOGGER.info("[START] application wake UP");
final WebLauncher launcher = new WebLauncher();
launcher.migrateDB();
launcher.process();
WebLauncher.LOGGER.info("end-configure the server & wait finish process:");
Thread.currentThread().join();
WebLauncher.LOGGER.info("STOP Key updater");
launcher.stopOther();
WebLauncher.LOGGER.info("STOP the REST server:");
}
public void plop(final String aaa) {
// List available Image Readers
WebLauncher.LOGGER.trace("Available Image Readers:");
final Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName(aaa);
while (readers.hasNext()) {
final ImageReader reader = readers.next();
WebLauncher.LOGGER.trace("Reader: " + reader.getOriginatingProvider().getDescription(null));
WebLauncher.LOGGER.trace("Reader CN: " + reader.getOriginatingProvider().getPluginClassName());
// ImageIO.deregisterServiceProvider(reader.getOriginatingProvider());
}
// List available Image Writers
WebLauncher.LOGGER.trace("\nAvailable Image Writers:");
final Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(aaa);
while (writers.hasNext()) {
final ImageWriter writer = writers.next();
WebLauncher.LOGGER.trace("Writer: " + writer.getOriginatingProvider().getDescription(null));
WebLauncher.LOGGER.trace("Writer CN: " + writer.getOriginatingProvider().getPluginClassName());
}
}
public void process() throws InterruptedException, DataAccessException {
ImageIO.scanForPlugins();
plop("jpeg");
plop("png");
plop("webmp");
plop("webp");
// ===================================================================
// Configure resources
// ===================================================================
final ResourceConfig rc = new ResourceConfig();
// add multipart models ..
rc.register(MultiPartFeature.class);
// global authentication system
rc.register(OptionFilter.class);
// remove cors ==> all time called by an other system...
rc.register(CORSFilter.class);
// register exception catcher
GenericCatcher.addAll(rc);
// add default resource:
rc.register(TestResourceValidator.class);
// enable jersey specific validations (@Valid
rc.register(ValidationFeature.class);
ContextGenericTools.addJsr310(rc);
// add jackson to be discover when we are ins standalone server
rc.register(JacksonFeature.class);
LOGGER.info(" ==> {}", new DbConfig());
LOGGER.info("OAuth service {}", getBaseURI());
this.server = GrizzlyHttpServerFactory.createHttpServer(getBaseURI(), rc);
final HttpServer serverLink = this.server;
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override
public void run() {
LOGGER.warn("Stopping server..");
serverLink.shutdownNow();
}
}, "shutdownHook"));
// ===================================================================
// start periodic update of the token ...
// ===================================================================
this.keyUpdater = new UpdateJwtPublicKey();
this.keyUpdater.start();
// ===================================================================
// run JERSEY
// ===================================================================
try {
this.server.start();
LOGGER.info("Jersey app started at {}", getBaseURI());
} catch (final Exception e) {
LOGGER.error("There was an error while starting Grizzly HTTP server.");
e.printStackTrace();
}
}
public void stop() {
if (this.server != null) {
this.server.shutdownNow();
this.server = null;
}
}
public void stopOther() {
this.keyUpdater.kill();
try {
this.keyUpdater.join(4000, 0);
} catch (final InterruptedException e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,19 @@
package test.kar.archidata.hybernateValidator;
import org.kar.archidata.tools.ConfigBaseVariable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class WebLauncherTest extends WebLauncher {
final private static Logger LOGGER = LoggerFactory.getLogger(WebLauncherTest.class);
public WebLauncherTest() {
LOGGER.debug("Configure REST system");
// for local test:
ConfigBaseVariable.apiAdress = "http://127.0.0.1:12345/test/api/";
// Enable the test mode permit to access to the test token (never use it in production).
ConfigBaseVariable.testMode = "true";
// ConfigBaseVariable.dbPort = "3306";
}
}

View File

@ -0,0 +1,21 @@
package test.kar.archidata.hybernateValidator.model;
import java.util.List;
import org.kar.archidata.annotation.checker.ReadOnlyField;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Size;
public class ValidatorModel {
@ReadOnlyField
public String value;
@Size(max = 5)
public String data;
@Valid
public List<ValidatorSubModel> multipleElement;
@Valid
public ValidatorSubModel subElement;
}

View File

@ -0,0 +1,8 @@
package test.kar.archidata.hybernateValidator.model;
import jakarta.validation.constraints.Size;
public class ValidatorSubModel {
@Size(min = 2)
public String data;
}

View File

@ -0,0 +1,32 @@
package test.kar.archidata.hybernateValidator.resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.annotation.security.PermitAll;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Min;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.MediaType;
import test.kar.archidata.apiExtern.resource.TestResource;
import test.kar.archidata.hybernateValidator.model.ValidatorModel;
@Path("/TestResourceValidator")
@Produces({ MediaType.APPLICATION_JSON })
public class TestResourceValidator {
private static final Logger LOGGER = LoggerFactory.getLogger(TestResource.class);
@POST
@PermitAll
@Consumes(MediaType.APPLICATION_JSON)
public void post(final @QueryParam("queryParametersName") @Min(5) Long value, final @Valid ValidatorModel data)
throws Exception {
return;
}
}