[FIX] test on DateTime is ended and solution in 90% operational
This commit is contained in:
parent
ecc8829e8c
commit
b12108ec00
@ -11,15 +11,16 @@ public class GenericCatcher {
|
|||||||
public static void addAll(final ResourceConfig rc) {
|
public static void addAll(final ResourceConfig rc) {
|
||||||
// Generic Json parsing error
|
// Generic Json parsing error
|
||||||
rc.register(JacksonExceptionCatcher.class);
|
rc.register(JacksonExceptionCatcher.class);
|
||||||
// Catch jakarta generic errors
|
|
||||||
rc.register(WebApplicationExceptionCatcher.class);
|
|
||||||
// Archidata exceptions
|
// Archidata exceptions
|
||||||
rc.register(InputExceptionCatcher.class);
|
rc.register(InputExceptionCatcher.class);
|
||||||
rc.register(SystemExceptionCatcher.class);
|
rc.register(SystemExceptionCatcher.class);
|
||||||
rc.register(FailExceptionCatcher.class);
|
rc.register(FailExceptionCatcher.class);
|
||||||
// generic Exception catcher
|
// generic Exception catcher
|
||||||
rc.register(ExceptionCatcher.class);
|
|
||||||
rc.register(ConstraintViolationExceptionCatcher.class);
|
rc.register(ConstraintViolationExceptionCatcher.class);
|
||||||
|
rc.register(QueryParamExceptionCatcher.class);
|
||||||
|
rc.register(ExceptionCatcher.class);
|
||||||
|
// Catch jakarta generic errors
|
||||||
|
rc.register(WebApplicationExceptionCatcher.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
package org.kar.archidata.catcher;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.glassfish.jersey.server.ParamException.QueryParamException;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import jakarta.ws.rs.core.Response;
|
||||||
|
import jakarta.ws.rs.ext.ExceptionMapper;
|
||||||
|
|
||||||
|
public class QueryParamExceptionCatcher implements ExceptionMapper<QueryParamException> {
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(QueryParamExceptionCatcher.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Response toResponse(final QueryParamException exception) {
|
||||||
|
LOGGER.trace("Catch IllegalArgumentException: {}", exception.getLocalizedMessage());
|
||||||
|
final RestErrorResponse ret = build(exception);
|
||||||
|
LOGGER.error("Error OID={}", ret.oid);
|
||||||
|
return Response.status(Response.Status.BAD_REQUEST).entity(ret).type(MediaType.APPLICATION_JSON).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private RestErrorResponse build(final QueryParamException exception) {
|
||||||
|
final List<RestInputError> inputError = new ArrayList<>();
|
||||||
|
inputError.add(new RestInputError("query", exception.getParameterName(), exception.getCause().getMessage()));
|
||||||
|
final String errorType = "Error on query input='" + exception.getParameterName() + "'";
|
||||||
|
return new RestErrorResponse(Response.Status.BAD_REQUEST, Instant.now().toString(), errorType,
|
||||||
|
"Input parsing fail", inputError);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -27,6 +27,17 @@ public class RestInputError {
|
|||||||
|
|
||||||
public RestInputError() {}
|
public RestInputError() {}
|
||||||
|
|
||||||
|
public RestInputError(final String argument, final String path, final String message) {
|
||||||
|
this.path = argument;
|
||||||
|
this.path = path;
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RestInputError(final String path, final String message) {
|
||||||
|
this.path = path;
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
public RestInputError(final Path path, final String message) {
|
public RestInputError(final Path path, final String message) {
|
||||||
final Matcher matcher = PATTERN.matcher(path.toString());
|
final Matcher matcher = PATTERN.matcher(path.toString());
|
||||||
if (matcher.find()) {
|
if (matcher.find()) {
|
||||||
@ -39,11 +50,6 @@ public class RestInputError {
|
|||||||
this.message = message;
|
this.message = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RestInputError(final String path, final String message) {
|
|
||||||
this.path = path;
|
|
||||||
this.message = message;
|
|
||||||
}
|
|
||||||
|
|
||||||
String getFullPath() {
|
String getFullPath() {
|
||||||
if (this.path == null) {
|
if (this.path == null) {
|
||||||
return this.argument;
|
return this.argument;
|
||||||
|
@ -1,51 +0,0 @@
|
|||||||
package org.kar.archidata.converter.Jakarta;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.lang.reflect.Type;
|
|
||||||
import java.time.OffsetDateTime;
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
import org.kar.archidata.tools.DateTools;
|
|
||||||
|
|
||||||
import jakarta.ws.rs.ext.ParamConverter;
|
|
||||||
import jakarta.ws.rs.ext.ParamConverterProvider;
|
|
||||||
import jakarta.ws.rs.ext.Provider;
|
|
||||||
|
|
||||||
@Provider
|
|
||||||
public class DateParamConverter implements ParamConverterProvider {
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
|
||||||
public <T> ParamConverter<T> getConverter(
|
|
||||||
final Class<T> rawType,
|
|
||||||
final Type genericType,
|
|
||||||
final Annotation[] annotations) {
|
|
||||||
if (rawType != OffsetDateTime.class) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return (ParamConverter<T>) new ParamConverter<Date>() {
|
|
||||||
@Override
|
|
||||||
public Date fromString(final String value) {
|
|
||||||
if (value == null || value.isEmpty()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
System.out.println(value);
|
|
||||||
try {
|
|
||||||
return DateTools.parseDate(value);
|
|
||||||
} catch (final IOException e) {
|
|
||||||
throw new IllegalArgumentException("Invalid date format. Please use ISO8601", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString(final Date value) {
|
|
||||||
if (value == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return DateTools.serializeMilliWithUTCTimeZone(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,53 @@
|
|||||||
|
package org.kar.archidata.converter.Jakarta;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import org.kar.archidata.tools.DateTools;
|
||||||
|
|
||||||
|
import jakarta.annotation.Priority;
|
||||||
|
import jakarta.ws.rs.ext.ParamConverter;
|
||||||
|
import jakarta.ws.rs.ext.ParamConverterProvider;
|
||||||
|
import jakarta.ws.rs.ext.Provider;
|
||||||
|
|
||||||
|
@Provider
|
||||||
|
@Priority(1)
|
||||||
|
public class DateParamConverterProvider implements ParamConverterProvider {
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public <T> ParamConverter<T> getConverter(
|
||||||
|
final Class<T> rawType,
|
||||||
|
final Type genericType,
|
||||||
|
final Annotation[] annotations) {
|
||||||
|
if (rawType != Date.class) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (ParamConverter<T>) new DateParamConverter();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DateParamConverter implements ParamConverter<Date> {
|
||||||
|
@Override
|
||||||
|
public Date fromString(final String value) {
|
||||||
|
if (value == null || value.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return DateTools.parseDate(value);
|
||||||
|
} catch (final IOException e) {
|
||||||
|
throw new IllegalArgumentException("Invalid date format. Please use ISO8601", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(final Date value) {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return DateTools.serializeMilliWithUTCTimeZone(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,50 +0,0 @@
|
|||||||
package org.kar.archidata.converter.Jakarta;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.lang.reflect.Type;
|
|
||||||
import java.time.OffsetDateTime;
|
|
||||||
|
|
||||||
import org.kar.archidata.tools.DateTools;
|
|
||||||
|
|
||||||
import jakarta.ws.rs.ext.ParamConverter;
|
|
||||||
import jakarta.ws.rs.ext.ParamConverterProvider;
|
|
||||||
import jakarta.ws.rs.ext.Provider;
|
|
||||||
|
|
||||||
@Provider
|
|
||||||
public class OffsetDateTimeParamConverter implements ParamConverterProvider {
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
|
||||||
public <T> ParamConverter<T> getConverter(
|
|
||||||
final Class<T> rawType,
|
|
||||||
final Type genericType,
|
|
||||||
final Annotation[] annotations) {
|
|
||||||
if (rawType != OffsetDateTime.class) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return (ParamConverter<T>) new ParamConverter<OffsetDateTime>() {
|
|
||||||
@Override
|
|
||||||
public OffsetDateTime fromString(final String value) {
|
|
||||||
if (value == null || value.isEmpty()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
System.out.println(value);
|
|
||||||
try {
|
|
||||||
return DateTools.parseOffsetDateTime(value);
|
|
||||||
} catch (final IOException e) {
|
|
||||||
throw new IllegalArgumentException("Invalid date format. Please use ISO8601", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString(final OffsetDateTime value) {
|
|
||||||
if (value == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return DateTools.serializeMilliWithUTCTimeZone(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,52 @@
|
|||||||
|
package org.kar.archidata.converter.Jakarta;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
|
||||||
|
import org.kar.archidata.tools.DateTools;
|
||||||
|
|
||||||
|
import jakarta.ws.rs.ext.ParamConverter;
|
||||||
|
import jakarta.ws.rs.ext.ParamConverterProvider;
|
||||||
|
import jakarta.ws.rs.ext.Provider;
|
||||||
|
|
||||||
|
@Provider
|
||||||
|
public class OffsetDateTimeParamConverterProvider implements ParamConverterProvider {
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public <T> ParamConverter<T> getConverter(
|
||||||
|
final Class<T> rawType,
|
||||||
|
final Type genericType,
|
||||||
|
final Annotation[] annotations) {
|
||||||
|
if (rawType != OffsetDateTime.class) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (ParamConverter<T>) new OffsetDateTimeParamConverter();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OffsetDateTimeParamConverter implements ParamConverter<OffsetDateTime> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OffsetDateTime fromString(final String value) {
|
||||||
|
if (value == null || value.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return DateTools.parseOffsetDateTime(value);
|
||||||
|
} catch (final IOException e) {
|
||||||
|
throw new IllegalArgumentException("Invalid date format. Please use ISO8601", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(final OffsetDateTime value) {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return DateTools.serializeMilliWithUTCTimeZone(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
package org.kar.archidata.filter;
|
package org.kar.archidata.filter;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@ -18,7 +20,12 @@ public class OptionFilter implements ContainerRequestFilter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void filter(final ContainerRequestContext requestContext) throws IOException {
|
public void filter(final ContainerRequestContext requestContext) throws IOException {
|
||||||
LOGGER.warn("Receive message from: [{}] {}", requestContext.getMethod(), requestContext.getUriInfo().getPath());
|
LOGGER.warn("Receive message from: [{}] '{}'", requestContext.getMethod(),
|
||||||
|
requestContext.getUriInfo().getPath());
|
||||||
|
final Map<String, List<String>> queryParams = requestContext.getUriInfo().getQueryParameters();
|
||||||
|
for (final Map.Entry<String, List<String>> entry : queryParams.entrySet()) {
|
||||||
|
LOGGER.warn("queryParam: '{}' => '{}'", entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
if (requestContext.getMethod().contentEquals("OPTIONS")) {
|
if (requestContext.getMethod().contentEquals("OPTIONS")) {
|
||||||
requestContext.abortWith(Response.status(Response.Status.NO_CONTENT).build());
|
requestContext.abortWith(Response.status(Response.Status.NO_CONTENT).build());
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,6 @@ package org.kar.archidata.tools;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.time.LocalTime;
|
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.time.ZoneOffset;
|
import java.time.ZoneOffset;
|
||||||
@ -20,44 +17,6 @@ import org.slf4j.LoggerFactory;
|
|||||||
public class DateTools {
|
public class DateTools {
|
||||||
private final static Logger LOGGER = LoggerFactory.getLogger(DateTools.class);
|
private final static Logger LOGGER = LoggerFactory.getLogger(DateTools.class);
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses a date string using a given pattern into a java.util.Date.
|
|
||||||
*
|
|
||||||
* @param inputDate the string to parse
|
|
||||||
* @param pattern the pattern to use (e.g., "yyyy-MM-dd")
|
|
||||||
* @return parsed Date object
|
|
||||||
* @throws ParseException if parsing fails
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public static Date parseDate(final String inputDate, final String pattern) throws ParseException {
|
|
||||||
final SimpleDateFormat format = new SimpleDateFormat(pattern);
|
|
||||||
return format.parse(inputDate);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Formats a java.util.Date using the specified format pattern.
|
|
||||||
*
|
|
||||||
* @param date the Date to format
|
|
||||||
* @param requiredDateFormat the format string (e.g., "yyyy-MM-dd'T'HH:mm:ss.SSSZ")
|
|
||||||
* @return formatted string
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public static String formatDate(final Date date, final String requiredDateFormat) {
|
|
||||||
final SimpleDateFormat df = new SimpleDateFormat(requiredDateFormat);
|
|
||||||
return df.format(date);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Formats a java.util.Date using a default ISO 8601-like format.
|
|
||||||
*
|
|
||||||
* @param date the Date to format
|
|
||||||
* @return formatted string in pattern "yyyy-MM-dd'T'HH:mm.ss.SSS'Z'"
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public static String formatDate(final Date date) {
|
|
||||||
return serializeMilliWithUTCTimeZone(date);
|
|
||||||
}
|
|
||||||
|
|
||||||
// List of supported parsers for flexible date string parsing.
|
// List of supported parsers for flexible date string parsing.
|
||||||
// Includes patterns with optional parts, slashes, and ISO standard formats.
|
// Includes patterns with optional parts, slashes, and ISO standard formats.
|
||||||
static final List<DateTimeFormatter> FORMATTERS = Arrays.asList(
|
static final List<DateTimeFormatter> FORMATTERS = Arrays.asList(
|
||||||
@ -77,39 +36,31 @@ public class DateTools {
|
|||||||
* @throws IOException if no supported format matches the input
|
* @throws IOException if no supported format matches the input
|
||||||
*/
|
*/
|
||||||
public static OffsetDateTime parseOffsetDateTime(final String dateString) throws IOException {
|
public static OffsetDateTime parseOffsetDateTime(final String dateString) throws IOException {
|
||||||
|
return parseOffsetDateTime(dateString, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static OffsetDateTime parseOffsetDateTime(final String dateString, final boolean missingAsUTC)
|
||||||
|
throws IOException {
|
||||||
|
if (dateString == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
for (final DateTimeFormatter formatter : FORMATTERS) {
|
for (final DateTimeFormatter formatter : FORMATTERS) {
|
||||||
try {
|
try {
|
||||||
return OffsetDateTime.parse(dateString, formatter);
|
return OffsetDateTime.parse(dateString, formatter);
|
||||||
} catch (final DateTimeParseException ex) {
|
} catch (final DateTimeParseException ex) {
|
||||||
// If the date string is missing a zone, try appending "Z"
|
if (missingAsUTC) {
|
||||||
try {
|
// If the date string is missing a zone, try appending "Z"
|
||||||
if (dateString.endsWith("Z") || dateString.endsWith("z")) {
|
try {
|
||||||
continue;
|
if (dateString.endsWith("Z") || dateString.endsWith("z")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return OffsetDateTime.parse(dateString + "Z", formatter);
|
||||||
|
} catch (final DateTimeParseException ex2) {
|
||||||
|
// Still failed, try next pattern
|
||||||
}
|
}
|
||||||
return OffsetDateTime.parse(dateString + "Z", formatter);
|
|
||||||
} catch (final DateTimeParseException ex2) {
|
|
||||||
// Still failed, try next pattern
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback: Try parsing as LocalDate (assume start of day UTC)
|
|
||||||
try {
|
|
||||||
final LocalDate dateTmp = LocalDate.parse(dateString);
|
|
||||||
return dateTmp.atStartOfDay(ZoneOffset.UTC).toInstant().atZone(ZoneOffset.UTC).toOffsetDateTime();
|
|
||||||
} catch (final DateTimeParseException e) {
|
|
||||||
// Ignore and try next fallback
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback: Try parsing as LocalTime (assume date is 0000-01-01 UTC)
|
|
||||||
try {
|
|
||||||
final LocalTime timeTmp = LocalTime.parse(dateString);
|
|
||||||
return OffsetDateTime.of(0, 1, 1, // year, month, day
|
|
||||||
timeTmp.getHour(), timeTmp.getMinute(), timeTmp.getSecond(), timeTmp.getNano(), ZoneOffset.UTC);
|
|
||||||
} catch (final DateTimeParseException e) {
|
|
||||||
// All parsing attempts failed
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new IOException("Unrecognized DATE format: '" + dateString + "' supported format ISO8601");
|
throw new IOException("Unrecognized DATE format: '" + dateString + "' supported format ISO8601");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,7 +73,7 @@ public class DateTools {
|
|||||||
* @throws ParseException if parsing fails entirely
|
* @throws ParseException if parsing fails entirely
|
||||||
*/
|
*/
|
||||||
public static Date parseDate(final String dateString) throws IOException {
|
public static Date parseDate(final String dateString) throws IOException {
|
||||||
final OffsetDateTime dateTime = parseOffsetDateTime(dateString);
|
final OffsetDateTime dateTime = parseOffsetDateTime(dateString, true);
|
||||||
dateTime.atZoneSameInstant(ZoneId.systemDefault());
|
dateTime.atZoneSameInstant(ZoneId.systemDefault());
|
||||||
return Date.from(dateTime.toInstant());
|
return Date.from(dateTime.toInstant());
|
||||||
}
|
}
|
||||||
@ -138,6 +89,9 @@ public class DateTools {
|
|||||||
* Example output: 2025-04-06T15:00:00.123+02:00
|
* Example output: 2025-04-06T15:00:00.123+02:00
|
||||||
*/
|
*/
|
||||||
public static String serializeMilliWithOriginalTimeZone(final OffsetDateTime offsetDateTime) {
|
public static String serializeMilliWithOriginalTimeZone(final OffsetDateTime offsetDateTime) {
|
||||||
|
if (offsetDateTime == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return offsetDateTime.format(PATTERN_MS_TIME_WITH_ZONE);
|
return offsetDateTime.format(PATTERN_MS_TIME_WITH_ZONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,6 +100,9 @@ public class DateTools {
|
|||||||
* then serializes it to a string with milliseconds and original timezone offset.
|
* then serializes it to a string with milliseconds and original timezone offset.
|
||||||
*/
|
*/
|
||||||
public static String serializeMilliWithOriginalTimeZone(final Date date) {
|
public static String serializeMilliWithOriginalTimeZone(final Date date) {
|
||||||
|
if (date == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return serializeMilliWithOriginalTimeZone(date.toInstant().atZone(ZoneId.systemDefault()).toOffsetDateTime());
|
return serializeMilliWithOriginalTimeZone(date.toInstant().atZone(ZoneId.systemDefault()).toOffsetDateTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,6 +116,9 @@ public class DateTools {
|
|||||||
* The offset is explicitly changed to UTC before formatting.
|
* The offset is explicitly changed to UTC before formatting.
|
||||||
*/
|
*/
|
||||||
public static String serializeMilliWithUTCTimeZone(final OffsetDateTime offsetDateTime) {
|
public static String serializeMilliWithUTCTimeZone(final OffsetDateTime offsetDateTime) {
|
||||||
|
if (offsetDateTime == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return offsetDateTime.withOffsetSameInstant(ZoneOffset.UTC).format(PATTERN_MS_TIME);
|
return offsetDateTime.withOffsetSameInstant(ZoneOffset.UTC).format(PATTERN_MS_TIME);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,6 +127,9 @@ public class DateTools {
|
|||||||
* then serializes it with milliseconds in UTC.
|
* then serializes it with milliseconds in UTC.
|
||||||
*/
|
*/
|
||||||
public static String serializeMilliWithUTCTimeZone(final Date date) {
|
public static String serializeMilliWithUTCTimeZone(final Date date) {
|
||||||
|
if (date == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return serializeMilliWithUTCTimeZone(date.toInstant().atZone(ZoneId.systemDefault()).toOffsetDateTime());
|
return serializeMilliWithUTCTimeZone(date.toInstant().atZone(ZoneId.systemDefault()).toOffsetDateTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,6 +143,9 @@ public class DateTools {
|
|||||||
* Serializes an OffsetDateTime to a string with nanosecond precision in UTC.
|
* Serializes an OffsetDateTime to a string with nanosecond precision in UTC.
|
||||||
*/
|
*/
|
||||||
public static String serializeNanoWithUTCTimeZone(final OffsetDateTime offsetDateTime) {
|
public static String serializeNanoWithUTCTimeZone(final OffsetDateTime offsetDateTime) {
|
||||||
|
if (offsetDateTime == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return offsetDateTime.withOffsetSameInstant(ZoneOffset.UTC).format(PATTERN_NS_TIME);
|
return offsetDateTime.withOffsetSameInstant(ZoneOffset.UTC).format(PATTERN_NS_TIME);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,6 +154,9 @@ public class DateTools {
|
|||||||
* then serializes it with nanoseconds in UTC.
|
* then serializes it with nanoseconds in UTC.
|
||||||
*/
|
*/
|
||||||
public static String serializeNanoWithUTCTimeZone(final Date date) {
|
public static String serializeNanoWithUTCTimeZone(final Date date) {
|
||||||
|
if (date == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return serializeNanoWithUTCTimeZone(date.toInstant().atZone(ZoneId.systemDefault()).toOffsetDateTime());
|
return serializeNanoWithUTCTimeZone(date.toInstant().atZone(ZoneId.systemDefault()).toOffsetDateTime());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,10 @@ public class RESTApi {
|
|||||||
this.token = token;
|
this.token = token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RESTApiRequest request() {
|
||||||
|
return request("");
|
||||||
|
}
|
||||||
|
|
||||||
public RESTApiRequest request(final String urlOffset) {
|
public RESTApiRequest request(final String urlOffset) {
|
||||||
return new RESTApiRequest(this.baseUrl + urlOffset, this.token);
|
return new RESTApiRequest(this.baseUrl + urlOffset, this.token);
|
||||||
}
|
}
|
||||||
|
@ -80,6 +80,20 @@ public class RESTApiRequest {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the request body as a raw String.
|
||||||
|
*
|
||||||
|
* @param body The raw string body (consider as "text/plain").
|
||||||
|
* @param contentType The content type of the request body.
|
||||||
|
* @return The updated RESTApiRequest instance.
|
||||||
|
*/
|
||||||
|
public <TYPE_BODY> RESTApiRequest bodyString(final String body) {
|
||||||
|
this.serializedBodyByte = null;
|
||||||
|
this.serializedBodyString = body;
|
||||||
|
this.contentType = "text/plain";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serializes a Map to a JSON string and sets it as the body.
|
* Serializes a Map to a JSON string and sets it as the body.
|
||||||
*
|
*
|
||||||
@ -121,6 +135,19 @@ public class RESTApiRequest {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sed data as a json body.
|
||||||
|
*
|
||||||
|
* @param body a serialized Json object.
|
||||||
|
* @return The updated RESTApiRequest instance.
|
||||||
|
* @throws JsonProcessingException If serialization fails.
|
||||||
|
*/
|
||||||
|
public <TYPE_BODY> RESTApiRequest bodyAsJson(final String body) {
|
||||||
|
this.serializedBodyString = body;
|
||||||
|
this.contentType = "application/json";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds a multipart/form-data request body from a map of fields.
|
* Builds a multipart/form-data request body from a map of fields.
|
||||||
* Handles both File and standard form fields.
|
* Handles both File and standard form fields.
|
||||||
@ -184,22 +211,6 @@ public class RESTApiRequest {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends a GET request and parses the response as a List of objects.
|
|
||||||
*
|
|
||||||
* @param clazz The class of the expected response elements.
|
|
||||||
* @return A list of parsed objects.
|
|
||||||
* @throws RESTErrorResponseException If an error response is received.
|
|
||||||
* @throws IOException If the request fails.
|
|
||||||
* @throws InterruptedException If the request is interrupted.
|
|
||||||
*/
|
|
||||||
public <TYPE_RESPONSE> List<TYPE_RESPONSE> gets(final Class<TYPE_RESPONSE> clazz)
|
|
||||||
throws RESTErrorResponseException, IOException, InterruptedException {
|
|
||||||
verb("GET");
|
|
||||||
final HttpRequest request = request();
|
|
||||||
return callAndParseRequestList(clazz, request);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the HTTP verb to GET.
|
* Sets the HTTP verb to GET.
|
||||||
*
|
*
|
||||||
@ -220,6 +231,16 @@ public class RESTApiRequest {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the HTTP verb to POST.
|
||||||
|
*
|
||||||
|
* @return The updated RESTApiRequest instance.
|
||||||
|
*/
|
||||||
|
public RESTApiRequest post() {
|
||||||
|
verb("POST");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the HTTP verb to PATCH.
|
* Sets the HTTP verb to PATCH.
|
||||||
*
|
*
|
||||||
@ -269,9 +290,9 @@ public class RESTApiRequest {
|
|||||||
* @throws IOException If the request fails.
|
* @throws IOException If the request fails.
|
||||||
* @throws InterruptedException If the request is interrupted.
|
* @throws InterruptedException If the request is interrupted.
|
||||||
*/
|
*/
|
||||||
public <TYPE_RESPONSE> List<TYPE_RESPONSE> requestList(final Class<TYPE_RESPONSE> clazz)
|
public <TYPE_RESPONSE> List<TYPE_RESPONSE> fetchList(final Class<TYPE_RESPONSE> clazz)
|
||||||
throws RESTErrorResponseException, IOException, InterruptedException {
|
throws RESTErrorResponseException, IOException, InterruptedException {
|
||||||
final HttpRequest request = request();
|
final HttpRequest request = fetch();
|
||||||
return callAndParseRequestList(clazz, request);
|
return callAndParseRequestList(clazz, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,12 +305,23 @@ public class RESTApiRequest {
|
|||||||
* @throws IOException If the request fails.
|
* @throws IOException If the request fails.
|
||||||
* @throws InterruptedException If the request is interrupted.
|
* @throws InterruptedException If the request is interrupted.
|
||||||
*/
|
*/
|
||||||
public <TYPE_RESPONSE> TYPE_RESPONSE request(final Class<TYPE_RESPONSE> clazz)
|
public <TYPE_RESPONSE> TYPE_RESPONSE fetch(final Class<TYPE_RESPONSE> clazz)
|
||||||
throws RESTErrorResponseException, IOException, InterruptedException {
|
throws RESTErrorResponseException, IOException, InterruptedException {
|
||||||
final HttpRequest request = request();
|
final HttpRequest request = fetch();
|
||||||
return callAndParseRequest(clazz, request);
|
return callAndParseRequest(clazz, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a query parameter string from a map of key-value pairs.
|
||||||
|
*
|
||||||
|
* <p>This method encodes each key and value using UTF-8 encoding to ensure that
|
||||||
|
* the resulting query string is safe for use in a URL. The encoded key-value pairs
|
||||||
|
* are then joined together with '&' separators.</p>
|
||||||
|
*
|
||||||
|
* @param params A map containing query parameter names and their corresponding values.
|
||||||
|
* Both keys and values will be URL-encoded.
|
||||||
|
* @return A URL-encoded query string (e.g., "name=John+Doe&age=30")
|
||||||
|
*/
|
||||||
public static String buildQueryParams(final Map<String, String> params) {
|
public static String buildQueryParams(final Map<String, String> params) {
|
||||||
return params.entrySet().stream().map(entry -> URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8) + "="
|
return params.entrySet().stream().map(entry -> URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8) + "="
|
||||||
+ URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8)).collect(Collectors.joining("&"));
|
+ URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8)).collect(Collectors.joining("&"));
|
||||||
@ -303,13 +335,13 @@ public class RESTApiRequest {
|
|||||||
* @throws IOException If body serialization fails.
|
* @throws IOException If body serialization fails.
|
||||||
* @throws InterruptedException If the request is interrupted.
|
* @throws InterruptedException If the request is interrupted.
|
||||||
*/
|
*/
|
||||||
public HttpRequest request() throws RESTErrorResponseException, IOException, InterruptedException {
|
public HttpRequest fetch() throws RESTErrorResponseException, IOException, InterruptedException {
|
||||||
Builder requestBuilding = null;
|
Builder requestBuilding = null;
|
||||||
final String queryParams = buildQueryParams(this.queryParam);
|
final String queryParams = buildQueryParams(this.queryParam);
|
||||||
if (queryParams != null && !queryParams.isEmpty()) {
|
if (queryParams == null || queryParams.isEmpty()) {
|
||||||
requestBuilding = createRequestBuilder(this.url);
|
requestBuilding = createRequestBuilder("");
|
||||||
} else {
|
} else {
|
||||||
requestBuilding = createRequestBuilder(this.url + "?" + queryParams);
|
requestBuilding = createRequestBuilder("?" + queryParams);
|
||||||
}
|
}
|
||||||
if (this.contentType != null) {
|
if (this.contentType != null) {
|
||||||
requestBuilding.header("Content-Type", this.contentType);
|
requestBuilding.header("Content-Type", this.contentType);
|
||||||
@ -336,7 +368,7 @@ public class RESTApiRequest {
|
|||||||
* @throws IOException If the request fails.
|
* @throws IOException If the request fails.
|
||||||
* @throws InterruptedException If the request is interrupted.
|
* @throws InterruptedException If the request is interrupted.
|
||||||
*/
|
*/
|
||||||
public HttpResponse<byte[]> getRaw(final String urlOffset) throws IOException, InterruptedException {
|
protected HttpResponse<byte[]> getRaw(final String urlOffset) throws IOException, InterruptedException {
|
||||||
final Builder requestBuilding = createRequestBuilder(urlOffset);
|
final Builder requestBuilding = createRequestBuilder(urlOffset);
|
||||||
final HttpRequest request = requestBuilding.method("GET", BodyPublishers.ofString("")).build();
|
final HttpRequest request = requestBuilding.method("GET", BodyPublishers.ofString("")).build();
|
||||||
final HttpClient client = HttpClient.newHttpClient();
|
final HttpClient client = HttpClient.newHttpClient();
|
||||||
@ -350,7 +382,7 @@ public class RESTApiRequest {
|
|||||||
* @param urlOffset The URL path relative to the base URL.
|
* @param urlOffset The URL path relative to the base URL.
|
||||||
* @return Initialized HttpRequest.Builder.
|
* @return Initialized HttpRequest.Builder.
|
||||||
*/
|
*/
|
||||||
public Builder createRequestBuilder(final String urlOffset) {
|
private Builder createRequestBuilder(final String urlOffset) {
|
||||||
Builder requestBuilding = HttpRequest.newBuilder().version(Version.HTTP_1_1)
|
Builder requestBuilding = HttpRequest.newBuilder().version(Version.HTTP_1_1)
|
||||||
.uri(URI.create(this.url + urlOffset));
|
.uri(URI.create(this.url + urlOffset));
|
||||||
if (this.token != null) {
|
if (this.token != null) {
|
||||||
@ -370,7 +402,7 @@ public class RESTApiRequest {
|
|||||||
* @throws InterruptedException If the request is interrupted.
|
* @throws InterruptedException If the request is interrupted.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public <TYPE_RESPONSE> TYPE_RESPONSE callAndParseRequest(
|
private <TYPE_RESPONSE> TYPE_RESPONSE callAndParseRequest(
|
||||||
final Class<TYPE_RESPONSE> clazzReturn,
|
final Class<TYPE_RESPONSE> clazzReturn,
|
||||||
final HttpRequest request) throws RESTErrorResponseException, IOException, InterruptedException {
|
final HttpRequest request) throws RESTErrorResponseException, IOException, InterruptedException {
|
||||||
final HttpClient client = HttpClient.newHttpClient();
|
final HttpClient client = HttpClient.newHttpClient();
|
||||||
@ -423,7 +455,7 @@ public class RESTApiRequest {
|
|||||||
* @throws InterruptedException If the request is interrupted.
|
* @throws InterruptedException If the request is interrupted.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public <TYPE_RESPONSE> List<TYPE_RESPONSE> callAndParseRequestList(
|
private <TYPE_RESPONSE> List<TYPE_RESPONSE> callAndParseRequestList(
|
||||||
final Class<TYPE_RESPONSE> clazzReturn,
|
final Class<TYPE_RESPONSE> clazzReturn,
|
||||||
final HttpRequest request) throws IOException, InterruptedException, RESTErrorResponseException {
|
final HttpRequest request) throws IOException, InterruptedException, RESTErrorResponseException {
|
||||||
final HttpClient client = HttpClient.newHttpClient();
|
final HttpClient client = HttpClient.newHttpClient();
|
||||||
|
@ -12,6 +12,7 @@ import org.junit.jupiter.api.Order;
|
|||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.TestMethodOrder;
|
import org.junit.jupiter.api.TestMethodOrder;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.kar.archidata.exception.RESTErrorResponseException;
|
||||||
import org.kar.archidata.tools.ConfigBaseVariable;
|
import org.kar.archidata.tools.ConfigBaseVariable;
|
||||||
import org.kar.archidata.tools.RESTApi;
|
import org.kar.archidata.tools.RESTApi;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@ -62,7 +63,8 @@ public class TestTime {
|
|||||||
data.localDate = LocalDate.now();
|
data.localDate = LocalDate.now();
|
||||||
data.localDateTime = LocalDateTime.now();
|
data.localDateTime = LocalDateTime.now();
|
||||||
|
|
||||||
final DataForJSR310 inserted = api.post(DataForJSR310.class, TestTime.ENDPOINT_NAME, data);
|
final DataForJSR310 inserted = api.request(TestTime.ENDPOINT_NAME).post().bodyJson(data)
|
||||||
|
.fetch(DataForJSR310.class);
|
||||||
Assertions.assertNotNull(inserted);
|
Assertions.assertNotNull(inserted);
|
||||||
Assertions.assertNotNull(inserted.localTime);
|
Assertions.assertNotNull(inserted.localTime);
|
||||||
Assertions.assertNotNull(inserted.localDate);
|
Assertions.assertNotNull(inserted.localDate);
|
||||||
@ -80,91 +82,88 @@ public class TestTime {
|
|||||||
"date": "2025-04-04T15:15:07.123"
|
"date": "2025-04-04T15:15:07.123"
|
||||||
}
|
}
|
||||||
""";
|
""";
|
||||||
String received = api.postJson(String.class, TestTime.ENDPOINT_NAME + "/serialize", data);
|
String received = api.request(TestTime.ENDPOINT_NAME + "/serialize").post().bodyAsJson(data)
|
||||||
LOGGER.info("received: '{}'", received);
|
.fetch(String.class);
|
||||||
Assertions.assertEquals("Fri Apr 04 15:15:07 UTC 2025", received);
|
Assertions.assertEquals("Fri Apr 04 15:15:07 UTC 2025", received);
|
||||||
data = """
|
data = """
|
||||||
{
|
{
|
||||||
"date": "2025-04-04T15:15:07.123Z"
|
"date": "2025-04-04T15:15:07.123Z"
|
||||||
}
|
}
|
||||||
""";
|
""";
|
||||||
received = api.postJson(String.class, TestTime.ENDPOINT_NAME + "/serialize", data);
|
received = api.request(TestTime.ENDPOINT_NAME + "/serialize").post().bodyAsJson(data).fetch(String.class);
|
||||||
LOGGER.info("received: '{}'", received);
|
|
||||||
Assertions.assertEquals("Fri Apr 04 15:15:07 UTC 2025", received);
|
Assertions.assertEquals("Fri Apr 04 15:15:07 UTC 2025", received);
|
||||||
data = """
|
data = """
|
||||||
{
|
{
|
||||||
"date": "2025-04-04T15:15:07.123+05:00"
|
"date": "2025-04-04T15:15:07.123+05:00"
|
||||||
}
|
}
|
||||||
""";
|
""";
|
||||||
received = api.postJson(String.class, TestTime.ENDPOINT_NAME + "/serialize", data);
|
received = api.request(TestTime.ENDPOINT_NAME + "/serialize").post().bodyAsJson(data).fetch(String.class);
|
||||||
LOGGER.info("received: '{}'", received);
|
|
||||||
Assertions.assertEquals("Fri Apr 04 10:15:07 UTC 2025", received);
|
Assertions.assertEquals("Fri Apr 04 10:15:07 UTC 2025", received);
|
||||||
|
|
||||||
Assertions.assertNotNull(received);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Order(3)
|
@Order(3)
|
||||||
@Test
|
@Test
|
||||||
public void unserializeValue() throws Exception {
|
public void unserializeValue() throws Exception {
|
||||||
String data = "2025-04-04T15:15:07.123Z";
|
String data = "2025-04-04T15:15:07.123Z";
|
||||||
DataForJSR310String received = api.postJson(DataForJSR310String.class, TestTime.ENDPOINT_NAME + "/unserialize",
|
DataForJSR310String received = api.request(TestTime.ENDPOINT_NAME + "/unserialize").post().bodyString(data)
|
||||||
data);
|
.fetch(DataForJSR310String.class);
|
||||||
LOGGER.info("send : '{}'", data);
|
Assertions.assertEquals("2025-04-04T15:15:07.123Z", received.date);
|
||||||
LOGGER.info("received: '{}'", received.date);
|
|
||||||
LOGGER.info("----------------------------------------------------");
|
|
||||||
data = "2025-04-04T15:15:07.123";
|
data = "2025-04-04T15:15:07.123";
|
||||||
received = api.postJson(DataForJSR310String.class, TestTime.ENDPOINT_NAME + "/unserialize", data);
|
received = api.request(TestTime.ENDPOINT_NAME + "/unserialize").post().bodyString(data)
|
||||||
LOGGER.info("send : '{}'", data);
|
.fetch(DataForJSR310String.class);
|
||||||
LOGGER.info("received: '{}'", received.date);
|
Assertions.assertEquals("2025-04-04T15:15:07.123Z", received.date);
|
||||||
LOGGER.info("----------------------------------------------------");
|
|
||||||
data = "2025-04-04T15:15:07.123+05:00";
|
data = "2025-04-04T15:15:07.123+05:00";
|
||||||
received = api.postJson(DataForJSR310String.class, TestTime.ENDPOINT_NAME + "/unserialize", data);
|
received = api.request(TestTime.ENDPOINT_NAME + "/unserialize").post().bodyString(data)
|
||||||
LOGGER.info("send : '{}'", data);
|
.fetch(DataForJSR310String.class);
|
||||||
LOGGER.info("received: '{}'", received.date);
|
Assertions.assertEquals("2025-04-04T10:15:07.123Z", received.date);
|
||||||
LOGGER.info("----------------------------------------------------");
|
|
||||||
//Assertions.assertEquals("Fri Apr 04 15:15:07 UTC 2025", received);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Order(50)
|
@Order(50)
|
||||||
@Test
|
@Test
|
||||||
public void jakartaInputDate() throws Exception {
|
public void jakartaInputDate() throws Exception {
|
||||||
String data = "2025-04-04T15:15:07.123Z";
|
String data = "2025-04-04T15:15:07.123Z";
|
||||||
String received = api.get(String.class, TestTime.ENDPOINT_NAME + "/inputDate");
|
String received = api.request(TestTime.ENDPOINT_NAME + "/inputDate").get().queryParam("date", data)
|
||||||
//String received = api.get(String.class, TestTime.ENDPOINT_NAME + "/inputDate?date=" + data);
|
.fetch(String.class);
|
||||||
LOGGER.info("send : '{}'", data);
|
Assertions.assertEquals("2025-04-04T15:15:07.123000000Z", received);
|
||||||
LOGGER.info("received: '{}'", received);
|
|
||||||
LOGGER.info("----------------------------------------------------");
|
|
||||||
data = "2025-04-04T15:15:07.123";
|
data = "2025-04-04T15:15:07.123";
|
||||||
received = api.get(String.class, TestTime.ENDPOINT_NAME + "/inputDate?date=" + data);
|
received = api.request(TestTime.ENDPOINT_NAME + "/inputDate").get().queryParam("date", data)
|
||||||
LOGGER.info("send : '{}'", data);
|
.fetch(String.class);
|
||||||
LOGGER.info("received: '{}'", received);
|
Assertions.assertEquals("2025-04-04T15:15:07.123000000Z", received);
|
||||||
LOGGER.info("----------------------------------------------------");
|
|
||||||
data = "2025-04-04T15:15:07.123+05:00";
|
data = "2025-04-04T15:15:07.123+05:00";
|
||||||
received = api.get(String.class, TestTime.ENDPOINT_NAME + "/inputDate?date=" + data);
|
received = api.request(TestTime.ENDPOINT_NAME + "/inputDate").get().queryParam("date", data)
|
||||||
LOGGER.info("send : '{}'", data);
|
.fetch(String.class);
|
||||||
LOGGER.info("received: '{}'", received);
|
Assertions.assertEquals("2025-04-04T10:15:07.123000000Z", received);
|
||||||
LOGGER.info("----------------------------------------------------");
|
|
||||||
//Assertions.assertEquals("Fri Apr 04 15:15:07 UTC 2025", received);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Order(51)
|
@Order(51)
|
||||||
@Test
|
@Test
|
||||||
public void jakartaInputOffsetDateTime() throws Exception {
|
public void jakartaInputOffsetDateTime() throws Exception {
|
||||||
String data = "2025-04-04T15:15:07.123Z";
|
String data = "2025-04-04T15:15:07.123Z";
|
||||||
String received = api.get(String.class, TestTime.ENDPOINT_NAME + "/inputOffsetDateTime?date=" + data);
|
String received = api.request(TestTime.ENDPOINT_NAME + "/inputOffsetDateTime").get().queryParam("date", data)
|
||||||
LOGGER.info("send : '{}'", data);
|
.fetch(String.class);
|
||||||
LOGGER.info("received: '{}'", received);
|
Assertions.assertEquals("2025-04-04T15:15:07.123000000Z", received);
|
||||||
LOGGER.info("----------------------------------------------------");
|
|
||||||
data = "2025-04-04T15:15:07.123";
|
// check with offset:
|
||||||
received = api.get(String.class, TestTime.ENDPOINT_NAME + "/inputOffsetDateTime?date=" + data);
|
|
||||||
LOGGER.info("send : '{}'", data);
|
|
||||||
LOGGER.info("received: '{}'", received);
|
|
||||||
LOGGER.info("----------------------------------------------------");
|
|
||||||
data = "2025-04-04T15:15:07.123+05:00";
|
data = "2025-04-04T15:15:07.123+05:00";
|
||||||
received = api.get(String.class, TestTime.ENDPOINT_NAME + "/inputOffsetDateTime?date=" + data);
|
received = api.request(TestTime.ENDPOINT_NAME + "/inputOffsetDateTime").get().queryParam("date", data)
|
||||||
LOGGER.info("send : '{}'", data);
|
.fetch(String.class);
|
||||||
LOGGER.info("received: '{}'", received);
|
Assertions.assertEquals("2025-04-04T10:15:07.123000000Z", received);
|
||||||
LOGGER.info("----------------------------------------------------");
|
|
||||||
//Assertions.assertEquals("Fri Apr 04 15:15:07 UTC 2025", received);
|
// Check parsing fail
|
||||||
|
final String dataFail = "2025-04-04T15:15:07.123";
|
||||||
|
final RESTErrorResponseException ex = Assertions.assertThrows(RESTErrorResponseException.class,
|
||||||
|
() -> api.request(TestTime.ENDPOINT_NAME + "/inputOffsetDateTime").get().queryParam("date", dataFail)
|
||||||
|
.fetch(String.class));
|
||||||
|
Assertions.assertEquals("Error on query input='date'", ex.name);
|
||||||
|
Assertions.assertEquals("Input parsing fail", ex.message);
|
||||||
|
Assertions.assertEquals(400, ex.status);
|
||||||
|
Assertions.assertNotNull(ex.inputError);
|
||||||
|
Assertions.assertEquals(1, ex.inputError.size());
|
||||||
|
Assertions.assertEquals("date", ex.inputError.get(0).path);
|
||||||
|
Assertions.assertEquals("Invalid date format. Please use ISO8601", ex.inputError.get(0).message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,17 @@
|
|||||||
package test.kar.archidata.apiExtern;
|
package test.kar.archidata.apiExtern;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
import org.junit.jupiter.api.MethodOrderer;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.TestMethodOrder;
|
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
|
||||||
import org.kar.archidata.tools.DateTools;
|
import org.kar.archidata.tools.DateTools;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import test.kar.archidata.StepwiseExtension;
|
|
||||||
|
|
||||||
@ExtendWith(StepwiseExtension.class)
|
|
||||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
|
||||||
public class TestTimeParsing {
|
public class TestTimeParsing {
|
||||||
private final static Logger LOGGER = LoggerFactory.getLogger(TestTime.class);
|
private final static Logger LOGGER = LoggerFactory.getLogger(TestTime.class);
|
||||||
|
|
||||||
@ -124,23 +118,9 @@ public class TestTimeParsing {
|
|||||||
parsed = DateTools.parseDate(data);
|
parsed = DateTools.parseDate(data);
|
||||||
Assertions.assertEquals("1999-01-30T18:16:17.123+09:00", DateTools.serializeMilliWithOriginalTimeZone(parsed));
|
Assertions.assertEquals("1999-01-30T18:16:17.123+09:00", DateTools.serializeMilliWithOriginalTimeZone(parsed));
|
||||||
Assertions.assertEquals("1999-01-30T09:16:17.123Z", DateTools.serializeMilliWithUTCTimeZone(parsed));
|
Assertions.assertEquals("1999-01-30T09:16:17.123Z", DateTools.serializeMilliWithUTCTimeZone(parsed));
|
||||||
data = "1999-01-30";
|
|
||||||
parsed = DateTools.parseDate(data);
|
|
||||||
Assertions.assertEquals("1999-01-30T09:00:00.000+09:00", DateTools.serializeMilliWithOriginalTimeZone(parsed));
|
|
||||||
Assertions.assertEquals("1999-01-30T00:00:00.000Z", DateTools.serializeMilliWithUTCTimeZone(parsed));
|
|
||||||
data = "18:16:17.123";
|
|
||||||
parsed = DateTools.parseDate(data);
|
|
||||||
Assertions.assertEquals("0001-01-02T03:35:16.123+09:18", DateTools.serializeMilliWithOriginalTimeZone(parsed));
|
|
||||||
Assertions.assertEquals("0001-01-01T18:16:17.123Z", DateTools.serializeMilliWithUTCTimeZone(parsed));
|
|
||||||
|
|
||||||
// data = "1999-01-30T18:16:17.123 UTC+09:00";
|
Assertions.assertThrows(IOException.class, () -> DateTools.parseOffsetDateTime("1999-01-30"));
|
||||||
// parsed = DateTools.parseDate2(data);
|
Assertions.assertThrows(IOException.class, () -> DateTools.parseOffsetDateTime("18:16:17.123"));
|
||||||
// LOGGER.info(">> send : '{}'", data);
|
|
||||||
// LOGGER.info(">> format OTZ: '{}'", DateTools.serializeMilliWithOriginalTimeZone(parsed));
|
|
||||||
// LOGGER.info(">> format UTC: '{}'", DateTools.serializeMilliWithUTCTimeZone(parsed));
|
|
||||||
// LOGGER.info("----------------------------------------------------");
|
|
||||||
// Assertions.assertEquals("", DateTools.serializeMilliWithOriginalTimeZone(parsed));
|
|
||||||
// Assertions.assertEquals("", DateTools.serializeMilliWithUTCTimeZone(parsed));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -160,18 +140,6 @@ public class TestTimeParsing {
|
|||||||
parsed = DateTools.parseOffsetDateTime(data);
|
parsed = DateTools.parseOffsetDateTime(data);
|
||||||
Assertions.assertEquals("1999-01-30T18:16:17.123+05:00", DateTools.serializeMilliWithOriginalTimeZone(parsed));
|
Assertions.assertEquals("1999-01-30T18:16:17.123+05:00", DateTools.serializeMilliWithOriginalTimeZone(parsed));
|
||||||
Assertions.assertEquals("1999-01-30T13:16:17.123Z", DateTools.serializeMilliWithUTCTimeZone(parsed));
|
Assertions.assertEquals("1999-01-30T13:16:17.123Z", DateTools.serializeMilliWithUTCTimeZone(parsed));
|
||||||
data = "1999-01-30T18:16:17.123456789";
|
|
||||||
parsed = DateTools.parseOffsetDateTime(data);
|
|
||||||
Assertions.assertEquals("1999-01-30T18:16:17.123Z", DateTools.serializeMilliWithOriginalTimeZone(parsed));
|
|
||||||
Assertions.assertEquals("1999-01-30T18:16:17.123Z", DateTools.serializeMilliWithUTCTimeZone(parsed));
|
|
||||||
data = "1999-01-30 18:16:17";
|
|
||||||
parsed = DateTools.parseOffsetDateTime(data);
|
|
||||||
Assertions.assertEquals("1999-01-30T18:16:17.000Z", DateTools.serializeMilliWithOriginalTimeZone(parsed));
|
|
||||||
Assertions.assertEquals("1999-01-30T18:16:17.000Z", DateTools.serializeMilliWithUTCTimeZone(parsed));
|
|
||||||
data = "1999-01-30T18:16:17";
|
|
||||||
parsed = DateTools.parseOffsetDateTime(data);
|
|
||||||
Assertions.assertEquals("1999-01-30T18:16:17.000Z", DateTools.serializeMilliWithOriginalTimeZone(parsed));
|
|
||||||
Assertions.assertEquals("1999-01-30T18:16:17.000Z", DateTools.serializeMilliWithUTCTimeZone(parsed));
|
|
||||||
data = "1999-01-30T18:16:17.123+09:00";
|
data = "1999-01-30T18:16:17.123+09:00";
|
||||||
parsed = DateTools.parseOffsetDateTime(data);
|
parsed = DateTools.parseOffsetDateTime(data);
|
||||||
Assertions.assertEquals("1999-01-30T18:16:17.123+09:00", DateTools.serializeMilliWithOriginalTimeZone(parsed));
|
Assertions.assertEquals("1999-01-30T18:16:17.123+09:00", DateTools.serializeMilliWithOriginalTimeZone(parsed));
|
||||||
@ -180,14 +148,13 @@ public class TestTimeParsing {
|
|||||||
parsed = DateTools.parseOffsetDateTime(data);
|
parsed = DateTools.parseOffsetDateTime(data);
|
||||||
Assertions.assertEquals("1999-01-30T18:16:17.123+09:00", DateTools.serializeMilliWithOriginalTimeZone(parsed));
|
Assertions.assertEquals("1999-01-30T18:16:17.123+09:00", DateTools.serializeMilliWithOriginalTimeZone(parsed));
|
||||||
Assertions.assertEquals("1999-01-30T09:16:17.123Z", DateTools.serializeMilliWithUTCTimeZone(parsed));
|
Assertions.assertEquals("1999-01-30T09:16:17.123Z", DateTools.serializeMilliWithUTCTimeZone(parsed));
|
||||||
data = "1999-01-30";
|
|
||||||
parsed = DateTools.parseOffsetDateTime(data);
|
Assertions.assertThrows(IOException.class,
|
||||||
Assertions.assertEquals("1999-01-30T00:00:00.000Z", DateTools.serializeMilliWithOriginalTimeZone(parsed));
|
() -> DateTools.parseOffsetDateTime("1999-01-30T18:16:17.123456789"));
|
||||||
Assertions.assertEquals("1999-01-30T00:00:00.000Z", DateTools.serializeMilliWithUTCTimeZone(parsed));
|
Assertions.assertThrows(IOException.class, () -> DateTools.parseOffsetDateTime("1999-01-30 18:16:17"));
|
||||||
data = "18:16:17.123";
|
Assertions.assertThrows(IOException.class, () -> DateTools.parseOffsetDateTime("1999-01-30T18:16:17"));
|
||||||
parsed = DateTools.parseOffsetDateTime(data);
|
Assertions.assertThrows(IOException.class, () -> DateTools.parseOffsetDateTime("1999-01-30"));
|
||||||
Assertions.assertEquals("0001-01-01T18:16:17.123Z", DateTools.serializeMilliWithOriginalTimeZone(parsed));
|
Assertions.assertThrows(IOException.class, () -> DateTools.parseOffsetDateTime("18:16:17.123"));
|
||||||
Assertions.assertEquals("0001-01-01T18:16:17.123Z", DateTools.serializeMilliWithUTCTimeZone(parsed));
|
|
||||||
|
|
||||||
// data = "1999-01-30T18:16:17.123 UTC+09:00";
|
// data = "1999-01-30T18:16:17.123 UTC+09:00";
|
||||||
// parsed = DateTools.parseDate2(data);
|
// parsed = DateTools.parseDate2(data);
|
||||||
|
@ -17,8 +17,8 @@ import org.kar.archidata.UpdateJwtPublicKey;
|
|||||||
import org.kar.archidata.api.DataResource;
|
import org.kar.archidata.api.DataResource;
|
||||||
import org.kar.archidata.api.ProxyResource;
|
import org.kar.archidata.api.ProxyResource;
|
||||||
import org.kar.archidata.catcher.GenericCatcher;
|
import org.kar.archidata.catcher.GenericCatcher;
|
||||||
import org.kar.archidata.converter.Jakarta.DateParamConverter;
|
import org.kar.archidata.converter.Jakarta.DateParamConverterProvider;
|
||||||
import org.kar.archidata.converter.Jakarta.OffsetDateTimeParamConverter;
|
import org.kar.archidata.converter.Jakarta.OffsetDateTimeParamConverterProvider;
|
||||||
import org.kar.archidata.db.DbConfig;
|
import org.kar.archidata.db.DbConfig;
|
||||||
import org.kar.archidata.exception.DataAccessException;
|
import org.kar.archidata.exception.DataAccessException;
|
||||||
import org.kar.archidata.filter.CORSFilter;
|
import org.kar.archidata.filter.CORSFilter;
|
||||||
@ -106,8 +106,8 @@ public class WebLauncher {
|
|||||||
final ResourceConfig rc = new ResourceConfig();
|
final ResourceConfig rc = new ResourceConfig();
|
||||||
|
|
||||||
// Add permissive date converter for jakarta
|
// Add permissive date converter for jakarta
|
||||||
rc.register(DateParamConverter.class);
|
rc.register(DateParamConverterProvider.class);
|
||||||
rc.register(OffsetDateTimeParamConverter.class);
|
rc.register(OffsetDateTimeParamConverterProvider.class);
|
||||||
|
|
||||||
// add multipart models ..
|
// add multipart models ..
|
||||||
rc.register(MultiPartFeature.class);
|
rc.register(MultiPartFeature.class);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user